#pragma once #include "core/ui/rect.h" // action_bar — layout + hit-test for the bank_panel's task-grouped toolbars: buttons cluster by // task (Capture/Placement/Maintenance/Tagging/Switching); on a narrow panel, whole trailing // buttons drop rather than shrink or clip. The destructive Prune button lives separately in // prune_button, kept out of this cluster on purpose. #include namespace reasampler::ui { // Task cluster a button belongs to. Cluster order is caller-supplied via ClusterSpec, not fixed // here; a slot just carries which cluster it landed in. enum class ActionCluster { Capture, Placement, Maintenance, Tagging, Switching, }; using ActionBarRect = Rect; // One visible button's placement, top-left origin. `index` is its position in the caller's flat // action list (cluster order), so index also selects the action to fire on a hit. Only buttons // that fit get a slot — overflow is dropped whole, never clipped. struct ActionBarSlot { int index = 0; ActionCluster cluster = ActionCluster::Capture; int x = 0; int y = 0; int width = 0; int height = 0; // Label sub-rect, full button height, horizontally inset so text clears the edge. int labelX = 0, labelY = 0, labelW = 0, labelH = 0; bool operator==(const ActionBarSlot& o) const { return index == o.index && cluster == o.cluster && x == o.x && y == o.y && width == o.width && height == o.height && labelX == o.labelX && labelY == o.labelY && labelW == o.labelW && labelH == o.labelH; } }; // One cluster's button count, in the order the caller wants it drawn. count == 0 skips the // cluster (no gap emitted). Flat action indices run cluster-by-cluster in this order. struct ClusterSpec { ActionCluster cluster = ActionCluster::Capture; int count = 0; }; // Layout inputs, in pixels; defaults are the bank_panel action-bar metrics. struct ActionBarSpec { int buttonWidth = 108; int buttonGap = 4; int clusterGap = 16; int sidePad = 8; int verticalInset = 3; }; // How many buttons (from the front) fit at spec.buttonWidth. Split out so the shell can size an // overflow affordance without re-deriving it. A bar too narrow for even one button yields 0. struct BarFit { int visibleCount = 0; int hiddenCount = 0; }; BarFit computeBarFit(const ActionBarRect& bar, const std::vector& clusters, const ActionBarSpec& spec); // Lays out the visible buttons (per computeBarFit) left-to-right in cluster order. std::vector computeBarSlots(const ActionBarRect& bar, const std::vector& clusters, const ActionBarSpec& spec); // Flat action index under (px, py), or -1 for a miss (outside the bar, in a gap, or past the // last visible button). Gaps are real dead-zones here, not resolved to the nearest button. int hitTestActionBar(int px, int py, const ActionBarRect& bar, const std::vector& clusters, const ActionBarSpec& spec); } // namespace reasampler::ui