]> uap-core.de Git - note.git/commitdiff
implement NewNotebookDialog
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 20 May 2026 18:48:56 +0000 (20:48 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 20 May 2026 18:48:56 +0000 (20:48 +0200)
application/src/backend.rs
application/src/newnotebook.rs
application/src/window.rs
ui-rs/src/ui/label.rs
ui-rs/src/ui/toolkit.rs

index c7f18c1e1beb20f1140835b4951a6fea03b04d09..86446926441cc8164865842569b25cda6692b3d2 100644 (file)
@@ -227,4 +227,29 @@ impl BackendHandle {
         });
         let _ = self.tx.send(cmd);
     }
+
+    /// Creates a collection/notebook in the specified parent
+    pub fn add_notebook<F>(&self, parent: &collection::Model, name: String, callback: F)
+    where F: FnOnce(Result<collection::Model, DbErr>) + Send + 'static {
+        let backend = self.backend.clone();
+        let parent_path = format!("{}/{}", parent.parent, parent.name);
+
+        let cmd = Box::pin(async move {
+            let profile_id = backend.current_profile.id;
+
+            let insert_notebook = collection::ActiveModel {
+                profile_id: Set(profile_id),
+                name: Set(name.to_string()),
+                parent: Set(parent_path),
+                icon: Set("".to_string()),
+                kind: Set(CollectionType::Notebook),
+
+                ..Default::default()
+            };
+
+            let result = insert_notebook.insert(&backend.db).await;
+            callback(result);
+        });
+        let _ = self.tx.send(cmd);
+    }
 }
index 395501bff9ea974f47342e4d1578fb7437bc30dd..1bb1aeb75da003544af108cbca56e203e4d47d78 100644 (file)
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-use ui_rs::{UiModel, UiActions};
+use ui_rs::{UiModel, ui_actions};
 use ui_rs::ui::*;
 use entity::collection::{Model as Collection};
+use crate::backend::{BackendHandle};
 
-#[derive(UiModel, UiActions, Default)]
+#[derive(UiModel)]
 struct NewNotebookDialog {
+    backend: BackendHandle,
+
+    #[bind]
     name: UiString,
+
+    #[bind]
+    message: UiString,
+
+    #[bind]
     groups: UiList<Collection>
 }
 
-pub fn new_notebook_dialog<T>(parent: &UiObject<T>) {
-    let data = NewNotebookDialog::default();
+#[ui_actions]
+impl NewNotebookDialog {
+    /// Creates a new notebook in the selected parent (group)
+    fn add_notebook(&mut self, obj: &UiObject<NewNotebookDialog>) {
+        if self.name.get().len() == 0 {
+            self.message.set("Name required");
+            return;
+        }
+
+        let i = self.groups.selected_index();
+        let collections = self.groups.data();
+        let parent = usize::try_from(i).ok().and_then(|idx| collections.get(idx));
+        if let Some(parent) = parent {
+            let proxy = obj.obj_proxy();
+
+            self.backend.add_notebook(parent, self.name.get(), |result|{
+                proxy.call_mainthread(|obj, nnd|{
+                    match result {
+                        Ok(_collection) => {
+                            // we actually don't need the new collection object, we force
+                            // a global reload
+                            nnd.backend.reload_collections();
+                            obj.close();
+                        }
+                        Err(err) => {
+                            let msg = format!("add_notebook failed: {}", err);
+                            nnd.message.set(msg.as_str());
+                        }
+                    }
+                });
+            });
+        } else {
+            self.message.set("No parent selected");
+        }
+    }
+}
+
+pub fn new_notebook_dialog<T>(parent: &UiObject<T>, groups: &Vec<Collection>, backend: BackendHandle) {
+    let data = NewNotebookDialog {
+        backend: backend,
+        name: Default::default(),
+        message: Default::default(),
+        groups: Default::default(),
+    };
 
     parent.dialogwindow_builder()
         .title("Add Notebook")
@@ -46,11 +97,21 @@ pub fn new_notebook_dialog<T>(parent: &UiObject<T>) {
         .lbutton1("Add")
         .rbutton4("Cancel")
         .default_button(1)
-        .onclick(|e|{
-            e.obj.close();
+        .onclick(|e: &mut Event<NewNotebookDialog>|{
+            if e.intval == 1 {
+                // button1 pressed
+                e.data.add_notebook(e.obj)
+            } else {
+                // button2 (Cancel) pressed
+                e.obj.close();
+            }
         })
         .create(data, |obj, data|
     {
+        // init data
+        data.groups.data().extend(groups.clone());
+
+        // create dialog UI
         obj.grid(|a| {
             a.margin(10);
             a.columnspacing(10);
@@ -62,12 +123,15 @@ pub fn new_notebook_dialog<T>(parent: &UiObject<T>) {
         }, |obj| {
             obj.grid_row(|obj| {
                 obj.rlabel_builder().label("Group").create();
-                obj.dropdown_builder::<Collection>().value(&data.groups).create();
+                obj.dropdown_builder::<Collection>().value(&data.groups).getvalue(|e, _col, _row| ListValue::String(e.name.clone()) ).create();
             });
             obj.grid_row(|obj| {
                 obj.rlabel_builder().label("Name").create();
                 obj.textfield_builder().value(&data.name).create();
             });
+            obj.grid_row(|obj| {
+                obj.llabel_builder().value(&data.message).colspan(2).create();
+            });
         });
     }).show();
 }
\ No newline at end of file
index c95d98f275bea34b18b6ba14605fe455d389b73e..fad873053a0ec6061e6de4add56fa8d83f599391 100644 (file)
@@ -35,8 +35,12 @@ use crate::newnotebook::new_notebook_dialog;
 
 #[derive(UiModel)]
 pub struct MainWindow {
+    pub obj: UiObjRef<MainWindow>,
+
     pub backend: BackendHandle,
 
+    groups: Vec<Collection>,
+
     #[bind]
     notebooks: UiSourceList<Collection>
 }
@@ -45,14 +49,16 @@ pub struct MainWindow {
 impl MainWindow {
     pub fn new(app: &App) -> MainWindow {
         MainWindow {
+            obj: UiObjRef::default(),
             backend: app.backend.clone(),
+            groups: Vec::new(),
             notebooks: UiSourceList::default()
         }
     }
 
     #[action]
     pub fn new_notebook(&mut self, event: &ActionEvent) {
-        new_notebook_dialog(event.obj);
+        new_notebook_dialog(event.obj, &self.groups, self.backend.clone());
     }
 
     #[action]
@@ -74,9 +80,29 @@ impl MainWindow {
     pub fn message(&mut self, _event: &ActionEvent) {
         while let Ok(msg) = self.backend.brx.try_recv() {
             match msg {
-                BroadcastMessage::NotebookStructureUpdate(nodes) => fill_sourcelist_from_nodes(&mut self.notebooks, &nodes),
+                BroadcastMessage::NotebookStructureUpdate(nodes) => self.update_notebook_structure(&nodes),
+            }
+        }
+    }
+
+    fn update_notebook_structure(&mut self, nodes: &Vec<Node>) {
+        self.notebooks.clear();
+        self.groups.clear();
+
+        for elm in nodes.iter() {
+            self.groups.push(elm.collection.clone());
+
+            let mut notebook = SubList::new();
+            notebook.set_header(elm.collection.name.as_str());
+
+            let sublist_data = notebook.list().data();
+            for child in elm.children.iter() {
+                sublist_data.push(child.collection.clone());
             }
+
+            self.notebooks.push(notebook);
         }
+        self.notebooks.update();
     }
 }
 
@@ -85,6 +111,7 @@ pub fn create_window(app: &App, ctx: &AppContext<MainWindow>) -> UiObject<MainWi
 
     let window = ctx.splitview_window("note", true, windowdata, |obj, data| {
         init_window_data(data, app);
+        data.obj = obj.obj_ref();
 
         obj.sidebar_builder().create(|obj|{
             obj.sourcelist(|b|{
@@ -133,22 +160,6 @@ pub fn create_window(app: &App, ctx: &AppContext<MainWindow>) -> UiObject<MainWi
 }
 
 fn init_window_data(window: &mut MainWindow, data: &App) {
-    fill_sourcelist_from_nodes(&mut window.notebooks, &data.notebooks);
+    window.update_notebook_structure(&data.notebooks);
 }
 
-fn fill_sourcelist_from_nodes(list: &mut UiSourceList<Collection>, nodes: &Vec<Node>) {
-    list.clear();
-
-    for elm in nodes.iter() {
-        let mut notebook = SubList::new();
-        notebook.set_header(elm.collection.name.as_str());
-
-        let sublist_data = notebook.list().data();
-        for child in elm.children.iter() {
-            sublist_data.push(child.collection.clone());
-        }
-
-        list.push(notebook);
-    }
-    list.update();
-}
index 9de206e8349832e3efef945ec58068c87fbf3ba8..9ce3b433641a7780d8852225c8b89a166edeade3 100644 (file)
@@ -210,10 +210,11 @@ impl<'a, T> LabelBuilder<'a, T> {
         self
     }
 
-    pub fn value(&mut self, value: &toolkit::UiString) {
+    pub fn value(&mut self, value: &toolkit::UiString) -> &mut Self{
         unsafe {
             ui_label_args_set_value(self.args, value.ptr);
         }
+        self
     }
 
     pub fn varname(&mut self, varname: &str) -> &mut Self {
index bf9b5680a881f11cb421038c79f1a9550f71d006..5ee295f98556a922fdda202f39a0b72ea0d50567 100644 (file)
@@ -207,7 +207,7 @@ impl<T> UiObject<T> {
         UiObjProxy::from_ptr(self.ptr)
     }
 
-    pub fn window_data<F>(&mut self, f: F)
+    pub unsafe fn window_data<F>(&self, f: F)
     where F: FnOnce(&mut T) {
         unsafe {
             let wdata_ptr: *mut T = ui_object_get_windowdata(self.ptr).cast();
@@ -339,12 +339,14 @@ impl<T> UiObjProxy<T> {
     }
 
     pub fn call_mainthread<F>(self, f: F)
-    where F: FnOnce(&mut T) + Send + 'static {
+    where F: FnOnce(&UiObject<T>, &mut T) + Send + 'static {
         call_mainthread(|| {
-            let mut obj: UiObject<T> = UiObject::from_ptr(self.ptr);
-            obj.window_data(|wdata| {
-                f(wdata);
-            });
+            let obj: UiObject<T> = UiObject::from_ptr(self.ptr);
+            unsafe {
+                obj.window_data(|wdata| {
+                    f(&obj, wdata);
+                });
+            }
             drop(self);
         });
     }