git.lucas.co / cce-secrets
secrets manager
git clone https://git.lucas.co/cce-secrets.git

commit11542f8c941aa9df937add6680735a159fd91759
parentb4321053c7
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-07 22:08
refactor: hold widgets in Owned boxes (cce-ui Owned<W>)

The UiContext registry keeps raw pointers to registered widgets, and a
widget that moved while registered (its Vec reallocating, its struct
returned by value) left one pointing at the old address. Each widget
field is now an Owned<Adapted<..>> — a heap box whose allocation, not the
widget, carries the liveness token the registry watches — so the
registered address never moves and is dropped from the registry when the
box is freed. Field access is unchanged through Deref; construction
sites wrap in Owned::new.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 src/main.rs | 65 +++++++++++++++++++++++++++++++------------------------------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 7946c37..1203f91 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,7 @@
 use secret_service::{EncryptionType, SecretService};
 use wayland_client::QueueHandle;
 
+use cce_ui::widget::Owned;
 use cce_ui::engine::{Application, EngineState, LogicalPosition, LogicalSize, WindowSettings};
 use cce_ui::widget::{
     Bounds, Button, ElementState, Key, KeyEvent, MouseButton, MouseScrollDelta, NamedKey,
@@ -707,23 +708,23 @@ fn tick_bar(activity: &mut ScrollbarActivity, dt: f32, overflowing: bool, draggi
 // ── Application ───────────────────────────────────────────────────────────
 
 struct SecretsApp {
-    search_box: cce_ui::widget::Adapted<TextBox>,
-    refresh_btn: cce_ui::widget::Adapted<Button>,
-    sync_btn: cce_ui::widget::Adapted<Button>,
-    new_btn: cce_ui::widget::Adapted<Button>,
-    reveal_btn: cce_ui::widget::Adapted<Button>,
-    copy_btn: cce_ui::widget::Adapted<Button>,
-    otp_btn: cce_ui::widget::Adapted<Button>,
-    edit_btn: cce_ui::widget::Adapted<Button>,
-    delete_btn: cce_ui::widget::Adapted<Button>,
-    save_btn: cce_ui::widget::Adapted<Button>,
-    cancel_btn: cce_ui::widget::Adapted<Button>,
+    search_box: Owned<cce_ui::widget::Adapted<TextBox>>,
+    refresh_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    sync_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    new_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    reveal_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    copy_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    otp_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    edit_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    delete_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    save_btn: Owned<cce_ui::widget::Adapted<Button>>,
+    cancel_btn: Owned<cce_ui::widget::Adapted<Button>>,
     // The entry form, top to bottom (Tab order).
-    title_box: cce_ui::widget::Adapted<TextBox>,
-    user_box: cce_ui::widget::Adapted<TextBox>,
-    url_box: cce_ui::widget::Adapted<TextBox>,
-    notes_box: cce_ui::widget::Adapted<TextBox>,
-    pass_box: cce_ui::widget::Adapted<TextBox>,
+    title_box: Owned<cce_ui::widget::Adapted<TextBox>>,
+    user_box: Owned<cce_ui::widget::Adapted<TextBox>>,
+    url_box: Owned<cce_ui::widget::Adapted<TextBox>>,
+    notes_box: Owned<cce_ui::widget::Adapted<TextBox>>,
+    pass_box: Owned<cce_ui::widget::Adapted<TextBox>>,
 
     mode: Mode,
     entries: Vec<EntryData>,
@@ -1072,22 +1073,22 @@ impl Application for SecretsApp {
     fn new(_qh: &QueueHandle<EngineState<Self>>, sender: calloop::channel::Sender<Self::Message>) -> Self {
         let (cmd_tx, cmd_rx) = std::sync::mpsc::channel();
         Self {
-            search_box: TextBox::new(String::new()).with_placeholder("Search"),
-            refresh_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Refresh"),
-            sync_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Sync"),
-            new_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("New"),
-            reveal_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Reveal"),
-            copy_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Copy"),
-            otp_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Copy code"),
-            edit_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Edit"),
-            delete_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Delete"),
-            save_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Save"),
-            cancel_btn: Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Cancel"),
-            title_box: TextBox::new(String::new()).with_placeholder("title"),
-            user_box: TextBox::new(String::new()).with_placeholder("username"),
-            url_box: TextBox::new(String::new()).with_placeholder("url"),
-            notes_box: TextBox::new(String::new()).with_placeholder("notes"),
-            pass_box: TextBox::new(String::new()).with_password(true).with_placeholder("password"),
+            search_box: Owned::new(TextBox::new(String::new()).with_placeholder("Search")),
+            refresh_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Refresh")),
+            sync_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Sync")),
+            new_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("New")),
+            reveal_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Reveal")),
+            copy_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Copy")),
+            otp_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Copy code")),
+            edit_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Edit")),
+            delete_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Delete")),
+            save_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Save")),
+            cancel_btn: Owned::new(Button::new(0.0, 0.0, BTN_W, cce_ui::layout::button_height()).with_label("Cancel")),
+            title_box: Owned::new(TextBox::new(String::new()).with_placeholder("title")),
+            user_box: Owned::new(TextBox::new(String::new()).with_placeholder("username")),
+            url_box: Owned::new(TextBox::new(String::new()).with_placeholder("url")),
+            notes_box: Owned::new(TextBox::new(String::new()).with_placeholder("notes")),
+            pass_box: Owned::new(TextBox::new(String::new()).with_password(true).with_placeholder("password")),
             mode: Mode::Browse,
             entries: Vec::new(),
             selected: None,