#include "PaneTree.h" #include #include namespace udpscope { PaneTree::PaneTree() : root_(new PaneNode()) {} void PaneTree::setRoot(std::unique_ptr node) { if (node) { root_ = std::move(node); } } double PaneTree::clampRatio(double ratio, double extent) { if (extent <= 2.0 * kMinPaneSize) { return 0.5; /* Too small to honour the minimum on both sides. */ } const double lo = kMinPaneSize / extent; return std::min(std::max(ratio, lo), 1.0 - lo); } void PaneTree::layoutNode(PaneNode* node, const Rect& r, std::vector& leaves, std::vector& splitters) { if (node == nullptr) { return; } if (node->leaf) { leaves.push_back(Placed{node, r}); return; } if (node->orient == Orient::Columns) { const double ratio = clampRatio(node->ratio, r.w); const double wA = r.w * ratio; layoutNode(node->a.get(), Rect{r.x, r.y, wA, r.h}, leaves, splitters); layoutNode(node->b.get(), Rect{r.x + wA, r.y, r.w - wA, r.h}, leaves, splitters); splitters.push_back(Splitter{ node, Rect{r.x + wA - kSplitterGrab * 0.5, r.y, kSplitterGrab, r.h}, Orient::Columns}); } else { const double ratio = clampRatio(node->ratio, r.h); const double hA = r.h * ratio; layoutNode(node->a.get(), Rect{r.x, r.y, r.w, hA}, leaves, splitters); layoutNode(node->b.get(), Rect{r.x, r.y + hA, r.w, r.h - hA}, leaves, splitters); splitters.push_back(Splitter{ node, Rect{r.x, r.y + hA - kSplitterGrab * 0.5, r.w, kSplitterGrab}, Orient::Rows}); } } void PaneTree::layout(const Rect& area, std::vector& leaves, std::vector& splitters) const { leaves.clear(); splitters.clear(); layoutNode(root_.get(), area, leaves, splitters); } void PaneTree::splitLeaf(PaneNode* leaf, Orient orient) { if (leaf == nullptr || !leaf->leaf) { return; } /* Move the existing content into a new first child; the second is empty. */ std::unique_ptr first(new PaneNode()); first->signals = std::move(leaf->signals); first->profilePane = leaf->profilePane; std::unique_ptr second(new PaneNode()); leaf->leaf = false; leaf->orient = orient; leaf->ratio = 0.5; leaf->signals.clear(); leaf->a = std::move(first); leaf->b = std::move(second); } PaneNode* PaneTree::findParent(PaneNode* node, const PaneNode* child) { if (node == nullptr || node->leaf) { return nullptr; } if (node->a.get() == child || node->b.get() == child) { return node; } if (PaneNode* p = findParent(node->a.get(), child)) { return p; } return findParent(node->b.get(), child); } void PaneTree::closeLeaf(PaneNode* leaf) { if (leaf == nullptr || !leaf->leaf) { return; } PaneNode* parent = findParent(root_.get(), leaf); if (parent == nullptr) { return; /* The root is the only leaf; a scope with no pane is useless. */ } std::unique_ptr survivor = (parent->a.get() == leaf) ? std::move(parent->b) : std::move(parent->a); /* Collapse the parent into the survivor in place, so the parent pointer * held by any caller stays valid. */ parent->leaf = survivor->leaf; parent->signals = std::move(survivor->signals); parent->profilePane = survivor->profilePane; parent->orient = survivor->orient; parent->ratio = survivor->ratio; parent->a = std::move(survivor->a); parent->b = std::move(survivor->b); } void PaneTree::setRatio(PaneNode* split, double ratio) { if (split != nullptr && !split->leaf) { split->ratio = std::min(std::max(ratio, 0.0), 1.0); } } size_t PaneTree::countLeaves(const PaneNode* node) { if (node == nullptr) { return 0; } if (node->leaf) { return 1; } return countLeaves(node->a.get()) + countLeaves(node->b.get()); } size_t PaneTree::leafCount() const { return countLeaves(root_.get()); } const PaneTree::Splitter* PaneTree::hitTestSplitter( const std::vector& splitters, double px, double py) const { for (const Splitter& s : splitters) { if (s.rect.contains(px, py)) { return &s; } } return nullptr; } Handle PaneTree::hitTestHandle(const Rect& pane, double px, double py) { if (!pane.contains(px, py)) { return Handle::None; } const double relX = px - pane.x; const double relY = py - pane.y; const double midY = pane.h * 0.5; const double midX = pane.w * 0.5; const double half = kHandleSize * 0.5; /* Close sits in the top-right corner and wins over the edge handles. */ if (relX >= pane.w - kHandleSize && relY <= kHandleSize) { return Handle::Close; } if (relX <= kHandleSize && std::abs(relY - midY) <= half * 3.0) { return Handle::Left; } if (relX >= pane.w - kHandleSize && std::abs(relY - midY) <= half * 3.0) { return Handle::Right; } if (relY <= kHandleSize && std::abs(relX - midX) <= half * 3.0) { return Handle::Top; } if (relY >= pane.h - kHandleSize && std::abs(relX - midX) <= half * 3.0) { return Handle::Bottom; } return Handle::None; } } /* namespace udpscope */