system settings
git clone https://git.lucas.co/cce-system-interface.git
accounts: Add, Edit and Delete are three icons in one row
Edit and Delete lived down in the selected-account zone, below the divider
that separates "the account list" from "this account's fields". They act on
the list, so they belong beside Add — a row of squares now: plus, pencil,
trash.
Icon faces mean the row can't be equal columns; a 26px glyph stretched across
a third of the section is not a button. It is drawn as ONE full-width cell
with the three placed inside it at fixed offsets, which needs the row's `y`
read ONCE up front: `ay()` reads the section's running content_y and every
button emitted advances it, which `add_row` resets between columns but not
within one. Re-reading it per button walked them diagonally down the page, a
button-height at a time — caught in the shadow, hence the comment.
Edit and Delete need a selection, so they appear only with one, omitted rather
than dimmed: an icon's only disabled state is opacity, and a faint square that
still takes the click reads as a control that ignored you. What is left below
the divider is Make Default and Re-login, and that row can now be EMPTY (a
default password account), so it is skipped rather than drawn as a bare gap.
Colours stay as they were — primary on Add, and Add still takes the accent
tint while its form is open, which is the only thing reporting that mode.
Unlike the services controls, nobody asked for these to go neutral.
Verified live in a shadow session across every branch: trio with a selection,
plus alone without one, the actions row present for a non-default OAuth
account and absent for a default password one. The pencil opened the edit form
for the right account and the trash deleted it — and the real
~/.config/cce/accounts.json is byte-identical either side of that click
(a0622e65…), because the shadow's isolated XDG_CONFIG_HOME is what
cce_config_dir resolves through. 63 tests pass.
src/app.rs | 12 ++++++++++
src/pages/accounts.rs | 62 +++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 62 insertions(+), 12 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index 2165dd8..4409aed 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -443,6 +443,10 @@ pub fn section_divider(sc: &mut cce_ui::layout::SectionContext<'_, PageContent>)
pub trait SectionContextExt {
fn button(&mut self, label: &str, x: f32, y: f32, w: f32, h: f32, bg: [f32; 4], hover_bg: [f32; 4], label_color: [f32; 4], action: AppAction);
fn button_left(&mut self, label: &str, x: f32, y: f32, w: f32, h: f32, bg: [f32; 4], hover_bg: [f32; 4], label_color: [f32; 4], action: AppAction);
+ /// [`PageContent::button_icon`] inside a section — `label` is the fallback
+ /// for a missing icon set, `alpha` dims the glyph.
+ #[allow(clippy::too_many_arguments)]
+ fn button_icon(&mut self, icon: &str, label: &str, x: f32, y: f32, w: f32, h: f32, bg: [f32; 4], hover_bg: [f32; 4], label_color: [f32; 4], alpha: f32, action: AppAction);
}
impl<'a> SectionContextExt for cce_ui::layout::SectionContext<'a, PageContent> {
@@ -461,6 +465,14 @@ impl<'a> SectionContextExt for cce_ui::layout::SectionContext<'a, PageContent> {
*height = height.max(self.content_y);
}
}
+
+ fn button_icon(&mut self, icon: &str, label: &str, x: f32, y: f32, w: f32, h: f32, bg: [f32; 4], hover_bg: [f32; 4], label_color: [f32; 4], alpha: f32, action: AppAction) {
+ self.pc.button_icon(icon, label, x, y, w, h, bg, hover_bg, label_color, alpha, action);
+ self.content_y = self.content_y.max(y + h);
+ for height in &mut self.grid.col_heights {
+ *height = height.max(self.content_y);
+ }
+ }
}
diff --git a/src/pages/accounts.rs b/src/pages/accounts.rs
index ab042d1..417690c 100644
--- a/src/pages/accounts.rs
+++ b/src/pages/accounts.rs
@@ -506,9 +506,44 @@ pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, sec_f
// the only clue.
let login_bg = if state.oauth_listener_running { (ACCENT_BG, ACCENT_BG) } else { BTN_NEUTRAL };
let narrow = item_w < 520.0;
- stack.add_row(1, 8.0, btn_h, |c, _, x, w| {
- c.button("Add Account", x, c.ay(), w, btn_h, add_bg.0, add_bg.1, TEXT_BTN,
+
+ // Add, Edit, Delete in one row of squares. Edit and Delete used to live
+ // down in the selected-account zone; they belong next to Add because
+ // all three act on the account LIST, while everything below the divider
+ // is about one account's fields.
+ //
+ // Icon faces, so each is a square the height of a button rather than a
+ // share of the section width — the row is drawn as one full-width cell
+ // with the buttons placed inside it, since equal columns would stretch
+ // a 26px glyph across a third of the section.
+ //
+ // Edit and Delete need a selection, so they appear only with one. They
+ // are OMITTED rather than dimmed: an icon's only disabled state is
+ // opacity, and a faint square that still takes the click reads as a
+ // control that ignored you. `narrow` still governs the fallback width —
+ // without an icon set these go back to being word buttons.
+ let sel = state.selected_idx.filter(|&i| i < state.accounts.len());
+ let icons_ok = cce_ui::upload_icon("plus", 32).is_some();
+ let (sq, bgap) = if icons_ok { (btn_h, 8.0) } else if narrow { (86.0, 6.0) } else { (110.0, 8.0) };
+ stack.add_row(1, 8.0, btn_h, |c, _, x, _w| {
+ // ONE y for the whole row. `c.ay()` reads the section's running
+ // content_y, and each button emitted advances it past its own
+ // bottom — normally right, because `add_row` resets content_y
+ // between COLUMNS. Three buttons inside a single column get no such
+ // reset, so re-reading `ay()` per button walked them diagonally
+ // down the page, one button-height at a time.
+ let y = c.ay();
+ c.button_icon("plus", "Add Account", x, y, sq, btn_h,
+ add_bg.0, add_bg.1, TEXT_BTN, 1.0,
AppAction::Accounts(AccountsMessage::AddAccountStart));
+ if let Some(i) = sel {
+ c.button_icon("pencil", "Edit", x + sq + bgap, y, sq, btn_h,
+ BTN_NEUTRAL.0, BTN_NEUTRAL.1, TEXT_BTN, 1.0,
+ AppAction::Accounts(AccountsMessage::EditAccountStart(i)));
+ c.button_icon("trash", "Delete", x + 2.0 * (sq + bgap), y, sq, btn_h,
+ BTN_DANGER.0, BTN_DANGER.1, TEXT_DANGER, 1.0,
+ AppAction::Accounts(AccountsMessage::DeleteAccount(i)));
+ }
});
section_divider(stack.context);
@@ -594,11 +629,13 @@ pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, sec_f
stack.context.spacing(6.0);
- // Per-account actions, compact; Delete quiet-red, at the end.
- // The row is sized to what is actually there — a fixed count
- // left ragged gaps whenever an account was default or password.
+ // What is left of the per-account actions once Edit and Delete
+ // moved up beside Add. The row is sized to what is actually
+ // there — a fixed count left ragged gaps whenever an account
+ // was default or password — and with only these two left it can
+ // now be EMPTY, for a default password account, so it is
+ // skipped rather than drawn as a bare gap.
let mut actions: Vec<(&str, ([f32; 4], [f32; 4]), [f32; 4], AccountsMessage)> = Vec::new();
- actions.push(("Edit", BTN_NEUTRAL, TEXT_BTN, AccountsMessage::EditAccountStart(selected_idx)));
if !acc.is_default {
actions.push(("Make Default", BTN_NEUTRAL, TEXT_BTN, AccountsMessage::MakeDefault(selected_idx)));
}
@@ -606,12 +643,13 @@ pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, sec_f
let relogin = if narrow { "Re-login" } else { "Re-login (Browser)" };
actions.push((relogin, login_bg, TEXT_BTN, AccountsMessage::GoogleLoginInit));
}
- actions.push(("Delete", BTN_DANGER, TEXT_DANGER, AccountsMessage::DeleteAccount(selected_idx)));
- stack.add_row(actions.len(), 8.0, btn_h, |c, i, x, w| {
- if let Some((label, colors, text_col, action)) = actions.get(i).cloned() {
- c.button(label, x, c.ay(), w, btn_h, colors.0, colors.1, text_col, AppAction::Accounts(action));
- }
- });
+ if !actions.is_empty() {
+ stack.add_row(actions.len(), 8.0, btn_h, |c, i, x, w| {
+ if let Some((label, colors, text_col, action)) = actions.get(i).cloned() {
+ c.button(label, x, c.ay(), w, btn_h, colors.0, colors.1, text_col, AppAction::Accounts(action));
+ }
+ });
+ }
}
} else {
stack.context.text("Select an account to view details, or add one.", 12.0, 0.0, 12.0, TEXT_DIM);