cloud storage client
git clone https://git.lucas.co/cce-cloud.git
fix(controls): launcher search, tabs and JSON popups at the toolkit's heights
The launcher's search well was a hand-set 35px and its Apps/System tab
run 22px; a JSON popup's checkbox rows were 22px with an 18px box. They
now ask cce-ui (textbox_height, button_height, toggle_height; the box is
square at its row, as a labelled Checkbox::field draws it), and the
popup-height estimate uses the same getters, including the spinbox and
colour heights the layout already used. Menu-styled button rows follow
the context menu's own ROW_H.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/json_layout.rs | 17 +++++++++++------
src/main.rs | 54 +++++++++++++++++++++++++++---------------------------
2 files changed, 38 insertions(+), 33 deletions(-)
diff --git a/src/json_layout.rs b/src/json_layout.rs
index 5226469..18b96cf 100644
--- a/src/json_layout.rs
+++ b/src/json_layout.rs
@@ -418,23 +418,28 @@ impl JsonLayoutWidget {
if w_state.widget_type == "checkbox" {
// set_rect is an WidgetHost method; call it on the box directly (the Phase 5
// Checkbox is an Adapted widget — as_any downcasts reach the model, not WidgetHost).
- w_state.widget.as_dyn_mut().set_rect(w_state.x, w_state.y + 2.0, 18.0, 18.0);
- w_state.h = 22.0;
+ // The toolkit's checkbox row: toggle height, the box square
+ // at it (as a labelled `Checkbox::field` draws it).
+ let h = cce_ui::layout::toggle_height();
+ w_state.widget.as_dyn_mut().set_rect(w_state.x, w_state.y, h, h);
+ w_state.h = h;
w_state.label_text = Some(TextLabel {
text: w_state.text.clone(),
- x: w_state.x + 28.0,
- y: w_state.y + 2.0,
+ x: w_state.x + h + 10.0,
+ y: w_state.y + (h - 18.0) / 2.0,
font_size: 13.0,
color: [0xcc, 0xcc, 0xd4],
});
} else {
let h = match w_state.widget_type.as_str() {
- "button" => 24.0 + top_room,
+ // Menu-styled rows (`new_menu_item`): the context menu's pitch.
+ "button" => cce_ui::widget::context_menu::ROW_H + top_room,
"label" => 18.0 + top_room,
"spinbox" => cce_ui::layout::spinbox_height() + top_room,
"color" | "rgb" | "rgba" => cce_ui::layout::color_selector_height() + top_room,
"slider" => 22.0 + top_room,
- _ => 24.0,
+ // An unknown type is built as a bare Button.
+ _ => cce_ui::layout::button_height(),
};
w_state.widget.as_dyn_mut().set_rect(w_state.x, w_state.y, usable_w, h);
w_state.h = h;
diff --git a/src/main.rs b/src/main.rs
index 130a1df..fcbb304 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -969,16 +969,14 @@ fn icon_image(name: &str) -> Option<(u32, u32, u32)> {
/// `let pad = 15.0;` locals in every one of the paint, scroll and hit-test
/// paths. The tab strip shifts the whole list down by its own height, so the
/// offset has to be derived in one place or the rows, the clip and the click
-/// go out of step.
-const SEARCH_H: f32 = 35.0;
+/// go out of step. The search well and the tab run are the toolkit's
+/// textbox and button heights (`cce_ui::layout::textbox_height` /
+/// `button_height`), read where they are used.
const ITEM_H: f32 = 25.0;
/// Row height once the list carries icons (Apps mode, the window switcher):
/// the icon plus a 5px standoff above and below. Text-only lists — dmenu,
/// Path, the System tab — keep the tighter [`ITEM_H`].
const ICON_ITEM_H: f32 = 36.0;
-/// Height of the tab strip's segmented run; the strip claims this plus one
-/// root-plate gap off the top (see [`FuzzelWidget::tab_strip_h`]).
-const TAB_RUN_H: f32 = 22.0;
/// style: deliberate — the hairline the selection chip (and the hover wash on
/// its footprint) stands in from the list viewport on each side, so the chip's
/// roll clears the clip. A standoff, not a rung of the spacing ladder.
@@ -1092,10 +1090,11 @@ impl FuzzelWidget {
self.switch_tab(idx)
}
- /// Height the tab strip takes off the top of the popup — the run and the
- /// gap between it and the search well; 0 below two tabs.
+ /// Height the tab strip takes off the top of the popup — the run (a
+ /// button's height) and the gap between it and the search well; 0 below
+ /// two tabs.
pub fn tab_strip_h(&self) -> f32 {
- if self.tabs.len() > 1 { TAB_RUN_H + cce_ui::layout::root_plate_gap() } else { 0.0 }
+ if self.tabs.len() > 1 { cce_ui::layout::button_height() + cce_ui::layout::root_plate_gap() } else { 0.0 }
}
/// The segmented run itself, inset from the popup edge like the search
@@ -1106,7 +1105,7 @@ impl FuzzelWidget {
x: self.x + inset,
y: self.y + inset,
width: self.w - inset * 2.0,
- height: TAB_RUN_H,
+ height: cce_ui::layout::button_height(),
})
}
@@ -1141,7 +1140,7 @@ impl FuzzelWidget {
/// Y of the list viewport's top edge — the number the scroll math, the row
/// hit-test, the clip and the empty-state label all have to agree on.
fn list_y(&self) -> f32 {
- self.search_y() + SEARCH_H + cce_ui::layout::root_plate_gap()
+ self.search_y() + cce_ui::layout::textbox_height() + cce_ui::layout::root_plate_gap()
}
/// Height of the list viewport: everything left between it and the bottom
@@ -1161,7 +1160,7 @@ impl FuzzelWidget {
/// well and the gap under it, and the tab strip when there is one. What
/// the popup's height is over its rows.
pub fn chrome_h(&self) -> f32 {
- 2.0 * cce_ui::layout::root_plate_inset() + self.tab_strip_h() + SEARCH_H + cce_ui::layout::root_plate_gap()
+ 2.0 * cce_ui::layout::root_plate_inset() + self.tab_strip_h() + cce_ui::layout::textbox_height() + cce_ui::layout::root_plate_gap()
}
/// Horizontal chrome around a row's text: the text inset on both sides.
@@ -1418,7 +1417,7 @@ impl cce_ui::widget::Paint for FuzzelWidget {
// highlight accent (the toolkit's focused-well treatment; the query
// line always holds keyboard focus here). Replaces the flat fill +
// 1px border quads.
- let search_h = SEARCH_H;
+ let search_h = cce_ui::layout::textbox_height();
let well = Rect { x: self.x + pad, y: self.search_y(), width: self.w - pad * 2.0, height: search_h };
ctx.quad(well, [0.10, 0.10, 0.14, 1.0]);
let depth = cce_ui::layout::bevel_width().min(search_h * 0.2);
@@ -1762,9 +1761,9 @@ fn json_widget_desired_width(widget_type: &str, text: &str) -> f32 {
}
"label" => cce_ui::widget::display::measure_text(text, 13.0) + margins,
// The remainders are each control's own width beside its label (the
- // checkbox's box column, the spinbox's field, the slider's track), not
- // spacing.
- "checkbox" => cce_ui::widget::display::measure_text(text, 13.0) + margins + 12.0,
+ // checkbox's toggle-height box and the 10px before its label, the
+ // spinbox's field, the slider's track), not spacing.
+ "checkbox" => cce_ui::widget::display::measure_text(text, 13.0) + margins + cce_ui::layout::toggle_height() + 10.0,
"spinbox" | "color" => cce_ui::widget::display::measure_text(text, 13.0) + margins + 88.0,
"slider" => cce_ui::widget::display::measure_text(text, 13.0) + margins + 128.0,
_ => 150.0,
@@ -1848,10 +1847,10 @@ impl State {
for w_conf in widgets {
let h = match w_conf.widget_type.as_str() {
"label" => 18.0,
- "checkbox" => 22.0,
- "button" => 24.0,
- "spinbox" => 22.0,
- "color" => 24.0,
+ "checkbox" => cce_ui::layout::toggle_height(),
+ "button" => cce_ui::widget::context_menu::ROW_H,
+ "spinbox" => cce_ui::layout::spinbox_height(),
+ "color" => cce_ui::layout::color_selector_height(),
_ => 20.0,
};
current_y += h + gap;
@@ -1863,10 +1862,10 @@ impl State {
for w_conf in &page.widgets {
let h = match w_conf.widget_type.as_str() {
"label" => 18.0,
- "checkbox" => 22.0,
- "button" => 24.0,
- "spinbox" => 22.0,
- "color" => 24.0,
+ "checkbox" => cce_ui::layout::toggle_height(),
+ "button" => cce_ui::widget::context_menu::ROW_H,
+ "spinbox" => cce_ui::layout::spinbox_height(),
+ "color" => cce_ui::layout::color_selector_height(),
_ => 20.0,
};
page_y += h + gap;
@@ -4854,10 +4853,10 @@ mod tests {
assert_eq!(w_label_h, 18.0);
assert_eq!(w_check_y, inset + 18.0 + gap); // y_prev + h_prev + spacing
- assert_eq!(w_check_h, 22.0);
+ assert_eq!(w_check_h, cce_ui::layout::toggle_height());
- assert_eq!(w_btn_y, w_check_y + 22.0 + gap);
- assert_eq!(w_btn_h, 24.0);
+ assert_eq!(w_btn_y, w_check_y + w_check_h + gap);
+ assert_eq!(w_btn_h, cce_ui::widget::context_menu::ROW_H);
// Check horizontal positioning (should match usable width: 300 - 2 * inset)
let usable_w = 300.0 - 2.0 * inset;
@@ -5354,7 +5353,8 @@ impl FuzzelWidget {
labels.push(TextLabel {
text: query_text,
x: self.text_x(),
- y: self.search_y() + 9.0,
+ // The 14px line centred in the well.
+ y: self.search_y() + (cce_ui::layout::textbox_height() - 17.0) / 2.0,
font_size: 14.0,
color: query_color,
});