Files
MARTe-Integrated-Components/Client/udpscope/PaneTree.h
T

117 lines
3.5 KiB
C++

/**
* @file PaneTree.h
* @brief Binary-space-partition layout of the plot area.
*
* Framework-free: no ImGui, no UDPS. The geometry and the hit-testing are the
* fiddly part of the pane UI and are unit-tested without a window.
*/
#pragma once
#include "Types.h"
#include <memory>
#include <string>
#include <vector>
namespace udpscope {
/** Direction a node splits its rectangle in. */
enum class Orient { Columns, Rows };
/** Vertical scaling strategy for one trace. */
enum class VMode { Auto, Range, Manual };
struct VScale {
VMode mode = VMode::Auto;
double div = 1.0; /**< Units per division, Manual only. */
double offset = 0.0; /**< Centre value, Manual only. */
};
/** One signal drawn in one pane. Signals are named, never indexed. */
struct Assignment {
std::string signalName;
Color color;
float lineWidth = 1.5f;
VScale vs;
};
/** Smallest a pane may be squeezed to, in pixels. */
constexpr double kMinPaneSize = 80.0;
/** Thickness of the splitter drag zone and of the inset handles, in pixels. */
constexpr double kSplitterGrab = 6.0;
constexpr double kHandleSize = 18.0;
/** What the pointer is over inside a pane. */
enum class Handle { None, Left, Right, Top, Bottom, Close };
struct PaneNode {
bool leaf = true;
/* leaf only */
std::vector<Assignment> signals;
bool profilePane = false; /**< Holds vector signals, not time series. */
/* split only */
Orient orient = Orient::Columns;
double ratio = 0.5; /**< First child's share of the parent. */
std::unique_ptr<PaneNode> a, b;
};
class PaneTree {
public:
struct Placed { PaneNode* leaf; Rect rect; };
struct Splitter { PaneNode* node; Rect rect; Orient orient; };
PaneTree();
PaneNode* root() { return root_.get(); }
const PaneNode* root() const { return root_.get(); }
/** Replaces the whole tree, e.g. when loading a session. */
void setRoot(std::unique_ptr<PaneNode> node);
/**
* @brief Walk the tree, producing every leaf's rectangle and every split's
* drag zone.
*/
void layout(const Rect& area,
std::vector<Placed>& leaves,
std::vector<Splitter>& splitters) const;
/** Turn a leaf into a split; the original content stays in the first child. */
void splitLeaf(PaneNode* leaf, Orient orient);
/** Replace the leaf's parent with its sibling. No-op on the last leaf. */
void closeLeaf(PaneNode* leaf);
void setRatio(PaneNode* split, double ratio);
size_t leafCount() const;
/** @return the splitter under the point, or nullptr. */
const Splitter* hitTestSplitter(const std::vector<Splitter>& splitters,
double px, double py) const;
/**
* @brief Which inset handle of @p pane the point is over.
*
* Handles sit inside the pane so they never overlap the splitter drag zone,
* and every pane has all four regardless of whether it touches a window
* edge — a pane in the middle of a 3x3 touches none.
*/
static Handle hitTestHandle(const Rect& pane, double px, double py);
private:
static void layoutNode(PaneNode* node, const Rect& r,
std::vector<Placed>& leaves,
std::vector<Splitter>& splitters);
static size_t countLeaves(const PaneNode* node);
static PaneNode* findParent(PaneNode* node, const PaneNode* child);
static double clampRatio(double ratio, double extent);
std::unique_ptr<PaneNode> root_;
};
} /* namespace udpscope */