app.toolbar_item("go_forward").icon(UiIconSet::GoForward.as_str()).action("go_forward").create();
app.toolbar_item("new_note").icon(UiIconSet::Add.as_str()).action("new_note").states(&[AppStates::NoteEnableNew as i32]).create();
+ app.toolbar_item("go_back2").icon(UiIconSet::GoBack.as_str()).action("go_back").create();
+ app.toolbar_item("go_forward2").icon(UiIconSet::GoForward.as_str()).action("go_forward").create();
+ app.toolbar_item("new_note2").icon(UiIconSet::Add.as_str()).action("new_note").states(&[AppStates::NoteEnableNew as i32]).create();
+
app.toolbar_add_default("new_notebook", ToolbarItemPosition::SidebarLeft);
app.toolbar_add_default("go_back", ToolbarItemPosition::Left);
app.toolbar_add_default("go_forward", ToolbarItemPosition::Left);
#[repr(i32)]
pub enum AppStates {
Null = 0,
- NoteEnableNew
+ NoteEnableNew,
+ NoteMaximized
}
fn username() -> String {
pub broadcast_rx: tokio::sync::broadcast::Receiver<BroadcastMessage>,
pub collection_id: i32,
- pub selected_note: Option<UiDoc<crate::note::Note>>,
- pub selected_index: Option<usize>,
+ pub selected_note: Option<NoteSelection>,
#[bind("notes")]
pub notes: UiList<NoteItem>
broadcast_rx: backend.btx.subscribe(),
collection_id: id,
selected_note: None,
- selected_index: None,
notes: Default::default()
}
}
// doc_ref.get_doc() should never fail here
if let Some(mut doc) = self.doc_ref.get_doc() {
// save the note
- let note_proxy = current.doc_proxy();
+ let note_proxy = current.doc.doc_proxy();
note_proxy.call_mainthread(move |_doc, note|{
note.save_note();
});
// detach the note
- doc.ctx.detach(current);
+ doc.ctx.detach(¤t.doc);
self.selected_note = None;
}
}
None
}
- pub fn select_note_from(&mut self, from: NoteSelectFrom) -> Option<()> {
+ pub fn get_note_id(&self, index: usize) -> NoteId {
+ if let Some(note) = self.notes.data().get(index) {
+ note.id.clone()
+ } else {
+ NoteId::TmpId(0) // placeholder id
+ }
+ }
+
+ pub fn is_current_selection(&self, note: &NoteId) -> bool{
+ if let Some(current) = &self.selected_note {
+ return current.id == *note
+ } else {
+ false
+ }
+ }
+
+ pub fn select_note_from(&mut self, from: NoteSelectFrom, maximized: bool) -> Option<()> {
let mut add_to_nav = false;
let (selected_index, note) = match from {
NoteSelectFrom::ListSelection(s) => {
let index = s.selected_index()?;
- if let Some(current_selection) = self.selected_index {
- if current_selection == index {
- return None
- }
+ if self.is_current_selection(&self.get_note_id(index)) {
+ return None
}
self.detach_current_note();
let note = s.selected_element_mut(self.notes.data_mut())?;
},
NoteSelectFrom::NavigationItem(nav) => {
let index = self.find_note(&nav.note_id?)?;
- if let Some(current_selection) = self.selected_index {
- if current_selection == index {
- return None
- }
+ if self.is_current_selection(&self.get_note_id(index)) {
+ return None
}
self.detach_current_note();
self.notes.select_with_event(index as i32, false);
(index, note)
}
};
-
- self.selected_index = Some(selected_index);
+
let is_new = note.is_new();
- let nav = NavigationItem { collection_id: self.collection_id, note_id: Some(note.id.clone()) };
+ let id = note.id.clone();
+ let nav = NavigationItem { collection_id: self.collection_id, note_id: Some(note.id.clone()), note_maximized: maximized };
let doc = if let Some(doc) = ¬e.model {
doc.clone()
} else {
if is_new {
nb.ctx.call_action("textarea_focus");
}
- self.selected_note = Some(doc);
+ self.selected_note = Some(NoteSelection { doc: doc, id: id, index: selected_index });
if add_to_nav {
let arg: Box<dyn Any> = Box::new(nav);
#[action]
pub fn note_selected(&mut self, event: &ActionEvent) {
if let EventType::ListSelection(s) = event.event_type {
- self.select_note_from(NoteSelectFrom::ListSelection(s));
+ self.select_note_from(NoteSelectFrom::ListSelection(s), false);
}
}
#[action]
- pub fn note_activated(&mut self, event: &mut ActionEvent) {
+ pub fn note_activated(&mut self, event: &mut ActionEvent) -> Option<()> {
+ let obj = &mut event.obj.as_mut()?;
+
if let EventType::ListSelection(s) = event.event_type {
- self.select_note_from(NoteSelectFrom::ListSelection(s));
- if let Some(obj) = &mut event.obj {
- obj.splitview_set_visible(0, false);
+ let result = self.select_note_from(NoteSelectFrom::ListSelection(s), true);
+ obj.splitview_set_visible(0, false);
+ // in case select_note_from didn't return a result, the note was already selected
+ // and no new navigation item was added
+ if result.is_none() {
+ let current = &self.selected_note.as_ref()?;
+ let nav = NavigationItem {
+ collection_id: self.collection_id,
+ note_id: Some(current.id.clone()),
+ note_maximized: true
+ };
+
+ let arg: Box<dyn Any> = Box::new(nav);
+ obj.ctx.call_action_with_parameter("navigation", arg);
}
}
+
+ Some(())
}
#[action]
}
#[action(NavigationItem)]
- pub fn navigate_to_note(&mut self, _event: &ActionEvent, nav: &NavigationItem) {
- println!("navigate to note");
+ pub fn navigate_to_note(&mut self, event: &mut ActionEvent, nav: &NavigationItem) {
+ let Some(obj) = &mut event.obj else {
+ return;
+ };
+
+ //println!("navigate to note");
if nav.collection_id != self.collection_id {
println!("invalid navigation event: notebook collection_id {} != {}", self.collection_id, nav.collection_id);
return;
}
- self.select_note_from(NoteSelectFrom::NavigationItem(nav.clone()));
+ self.select_note_from(NoteSelectFrom::NavigationItem(nav.clone()), false);
+ obj.splitview_set_visible(0, !nav.note_maximized);
}
pub fn update_note_title(&mut self, update: &NoteTitleUpdate) {
// Find the note in the list (or add it if it doesn't exist yet)
// Start with the currently selected note
let mut update_row: Option<usize> = None;
- if let Some(index) = self.selected_index {
- if let Some(item) = self.notes.data_mut().get_mut(index) {
+ if let Some(current) = &self.selected_note {
+ if let Some(item) = self.notes.data_mut().get_mut(current.index) {
if item.id == update.note_id {
item.data.title = update.title.clone();
- update_row = Some(index);
+ update_row = Some(current.index);
}
}
}
}
}
+
/// ListView item
pub struct NoteItem {
id: NoteId,
pub fn is_new(&self) -> bool {
return matches!(self.id, NoteId::TmpId(_))
}
+}
+
+pub struct NoteSelection {
+ pub id: NoteId,
+ pub doc: UiDoc<crate::note::Note>,
+ pub index: usize
}
\ No newline at end of file
b.onactivate(|e|{
if let EventType::SubList(sl) = &e.event_type {
if let Some(nb) = sl.get_mut_from(&mut e.data.notebooks) {
- let nav = NavigationItem { collection_id: nb.data.collection_id, note_id: None };
+ let nav = NavigationItem { collection_id: nb.data.collection_id, ..Default::default() };
// detach current notebook
if let Some(current) = &e.data.selected_notebook {
current.ctx.call_action("save");
#[derive(Default, Clone)]
pub struct NavigationItem {
pub collection_id: i32,
- pub note_id: Option<NoteId>
+ pub note_id: Option<NoteId>,
+ pub note_maximized: bool
}
pub enum NavDirection {
ctx.add_action(
stringify!(#name),
|target: &mut Self, event| {
- if let EventType::TypedObject(obj) = &event.event_type {
+ let event_type = std::mem::replace(&mut event.event_type, EventType::Null);
+ if let EventType::TypedObject(obj) = event_type {
if let Some(arg) = obj.downcast_ref::<#ty>() {
target.#name(event, arg);
}
#include "button.h"
#include "menu.h"
+#include "../ui/widget.h"
+
+#include <cx/array_list.h>
#include "../ui/properties.h"
}
// main toolbar
- ui_headerbar_add_items(obj, main_headerbar, left_defaults, UI_TOOLBAR_LEFT);
- ui_headerbar_add_items(obj, main_headerbar, center_defaults, UI_TOOLBAR_CENTER);
+ ui_headerbar_add_items(obj, main_headerbar, left_defaults, UI_TOOLBAR_LEFT, NULL);
+ ui_headerbar_add_items(obj, main_headerbar, center_defaults, UI_TOOLBAR_CENTER, NULL);
if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHT) {
ui_add_headerbar_menu(main_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
}
- ui_headerbar_add_items(obj, main_headerbar, right_defaults, UI_TOOLBAR_RIGHT);
+ ui_headerbar_add_items(obj, main_headerbar, right_defaults, UI_TOOLBAR_RIGHT, NULL);
// sidebar
if(sidebar_headerbar) {
// ui_headerbar_add_items pos parameter uses only UI_TOOLBAR_LEFT, UI_TOOLBAR_CENTER, UI_TOOLBAR_RIGHT
- ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_left_defaults, UI_TOOLBAR_LEFT);
+ ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_left_defaults, UI_TOOLBAR_LEFT, NULL);
if(appmenu && appmenu_pos == UI_TOOLBAR_SIDEBAR_RIGHT) {
ui_add_headerbar_menu(sidebar_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
}
- ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_right_defaults, UI_TOOLBAR_RIGHT);
+ ui_headerbar_add_items(obj, sidebar_headerbar, sidebar_right_defaults, UI_TOOLBAR_RIGHT, NULL);
}
// right panel
if(right_headerbar) {
- ui_headerbar_add_items(obj, right_headerbar, rightpanel_left_defaults, UI_TOOLBAR_LEFT);
- ui_headerbar_add_items(obj, right_headerbar, rightpanel_center_defaults, UI_TOOLBAR_CENTER);
+ // also add central toolbar items, in case the central pane is hidden
+ CxList *backup_toolbar_widgets = cxArrayListCreate(obj->ctx->allocator, CX_STORE_POINTERS, 16);
+ ui_headerbar_add_items(obj, right_headerbar, left_defaults, UI_TOOLBAR_LEFT, backup_toolbar_widgets);
+ ui_headerbar_add_items(obj, right_headerbar, center_defaults, UI_TOOLBAR_LEFT, backup_toolbar_widgets);
+ ui_headerbar_add_items(obj, right_headerbar, right_defaults, UI_TOOLBAR_LEFT, backup_toolbar_widgets);
+ CxIterator i = cxListIterator(backup_toolbar_widgets);
+ cx_foreach(GtkWidget *, w, i) {
+ ui_set_visible(w, FALSE);
+ }
+ g_object_set_data(G_OBJECT(obj->widget), "ui_toolbar_center_backup", backup_toolbar_widgets);
+
+ // main items for the right panel
+ ui_headerbar_add_items(obj, right_headerbar, rightpanel_left_defaults, UI_TOOLBAR_LEFT, NULL);
+ ui_headerbar_add_items(obj, right_headerbar, rightpanel_center_defaults, UI_TOOLBAR_CENTER, NULL);
if(appmenu && appmenu_pos == UI_TOOLBAR_RIGHTPANEL_RIGHT) {
ui_add_headerbar_menu(right_headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT);
}
- ui_headerbar_add_items(obj, right_headerbar, rightpanel_right_defaults, UI_TOOLBAR_RIGHT);
+ ui_headerbar_add_items(obj, right_headerbar, rightpanel_right_defaults, UI_TOOLBAR_RIGHT, NULL);
}
}
-static void create_item(UiObject *obj, GtkWidget *headerbar, GtkWidget *box, UiToolbarItemI *i, enum UiToolbarPos pos) {
+static GtkWidget* create_item(UiObject *obj, GtkWidget *headerbar, GtkWidget *box, UiToolbarItemI *i, enum UiToolbarPos pos) {
switch(i->type) {
case UI_TOOLBAR_ITEM: {
- ui_add_headerbar_item(headerbar, box, (UiToolbarItem*)i, obj, pos);
- break;
+ return ui_add_headerbar_item(headerbar, box, (UiToolbarItem*)i, obj, pos);
}
case UI_TOOLBAR_TOGGLEITEM: {
- ui_add_headerbar_toggleitem(headerbar, box, (UiToolbarToggleItem*)i, obj, pos);
- break;
+ return ui_add_headerbar_toggleitem(headerbar, box, (UiToolbarToggleItem*)i, obj, pos);
}
case UI_TOOLBAR_MENU: {
- ui_add_headerbar_menu(headerbar, box, (UiToolbarMenuItem*)i, obj, pos);
- break;
+ return ui_add_headerbar_menu(headerbar, box, (UiToolbarMenuItem*)i, obj, pos);
}
default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type);
}
+ return NULL;
}
static void headerbar_add(GtkWidget *headerbar, GtkWidget *box, GtkWidget *item, enum UiToolbarPos pos) {
}
}
-void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos) {
+void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos, CxList *out_items) {
GtkWidget *box = NULL;
if(pos == UI_TOOLBAR_CENTER && cxListSize(items) > 0) {
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
UI_HEADERBAR_SET_TITLE_WIDGET(headerbar, box);
+ if(out_items) {
+ cxListAdd(out_items, box);
+ }
}
CxIterator i = pos == UI_TOOLBAR_RIGHT ? cxListBackwardsIterator(items) : cxListIterator(items);
fprintf(stderr, "unknown toolbar item: %s\n", def);
continue;
}
- create_item(obj, headerbar, box, item, pos);
+ GtkWidget *item_widget = create_item(obj, headerbar, box, item, pos);
+ if(out_items && !box) {
+ cxListAdd(out_items, item_widget);
+ }
}
}
-void ui_add_headerbar_item(
+GtkWidget* ui_add_headerbar_item(
GtkWidget *headerbar,
GtkWidget *box,
UiToolbarItem *item,
ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
WIDGET_ADD_CSS_CLASS(button, "flat");
headerbar_add(headerbar, box, button, pos);
+ return button;
}
-void ui_add_headerbar_toggleitem(
+GtkWidget* ui_add_headerbar_toggleitem(
GtkWidget *headerbar,
GtkWidget *box,
UiToolbarToggleItem *item,
WIDGET_ADD_CSS_CLASS(button, "flat");
ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.tooltip, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, item->args.action, 0);
headerbar_add(headerbar, box, button, pos);
+ return button;
}
-void ui_add_headerbar_menu(
+GtkWidget* ui_add_headerbar_menu(
GtkWidget *headerbar,
GtkWidget *box,
UiToolbarMenuItem *item,
#endif
headerbar_add(headerbar, box, menubutton, pos);
+
+ return menubutton;
}
#endif // GTK_CHECK_VERSION(3, 10, 0)
void ui_fill_headerbar(UiObject *obj, GtkWidget *sidebar_headerbar, GtkWidget *main_headerbar, GtkWidget *right_headerbar);
-void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos);
+void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos, CxList *out_items);
-void ui_add_headerbar_item(
+GtkWidget* ui_add_headerbar_item(
GtkWidget *headerbar,
GtkWidget *box,
UiToolbarItem *item,
UiObject *obj,
enum UiToolbarPos pos);
-void ui_add_headerbar_toggleitem(
+GtkWidget* ui_add_headerbar_toggleitem(
GtkWidget *headerbar,
GtkWidget *box,
UiToolbarToggleItem *item,
UiObject *obj,
enum UiToolbarPos pos);
-void ui_add_headerbar_menu(
+GtkWidget* ui_add_headerbar_menu(
GtkWidget *headerbar,
GtkWidget *box,
UiToolbarMenuItem *item,
}
// event handling
- printf("test\n");
if(args->onactivate || args->onactivate_action) {
- printf("signal connect activate\n");
// columnview and listview can use the same callback function, because
// the first parameter (which is technically a different pointer type)
// is ignored
void ui_columnview_activate(void *ignore, guint position, gpointer userdata) {
- printf("columnview activate\n");
UiListView *view = userdata;
if(view->selection.count == 0) {
listview_update_selection(view);
#endif
}
-void ui_set_visible(UIWIDGET widget, UiBool visible) {
+void ui_set_visible(UIWIDGET widget, int visible) {
#if GTK_MAJOR_VERSION >= 4
gtk_widget_set_visible(widget, visible);
#else
#include "headerbar.h"
#include "button.h"
#include "window.h"
+#include "widget.h"
static int nwindows = 0;
g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel_top", toolbar_view);
g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel_top", right_panel);
}
-
+
GtkWidget *content_box = vbox;
-
+
if(sidebar) {
GtkWidget *splitview = adw_overlay_split_view_new();
adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), splitview);
-
+
GtkWidget *sidebar_toolbar_view = adw_toolbar_view_new();
adw_overlay_split_view_set_sidebar(ADW_OVERLAY_SPLIT_VIEW(splitview), sidebar_toolbar_view);
headerbar_sidebar = adw_header_bar_new();
adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), headerbar_sidebar);
-
+
adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), content);
-
+
g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_toolbar_view);
} else {
adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), content);
}
-
+
const char *show_title = ui_get_property("ui.gtk.window.showtitle");
if(show_title) {
if(!strcmp(show_title, "main") && sidebar) {
adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), TRUE);
}
}
-
+
adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar_main);
g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar_main);
-
+
if(!simple) {
ui_fill_headerbar(obj, headerbar_sidebar, headerbar_main, headerbar_right);
}
//GtkWidget *hb = ui_create_headerbar(obj);
//gtk_window_set_titlebar(GTK_WINDOW(obj->widget), hb);
}
-
+
GtkWidget *content_box = ui_gtk_vbox_new(0);
WINDOW_SET_CONTENT(obj->widget, vbox);
if(sidebar || splitview) {
g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
gtk_paned_set_position(GTK_PANED(paned), 200);
}
-
+
if(splitview) {
GtkWidget *content_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
gtk_paned_set_position(GTK_PANED(content_paned), splitview_pos);
PANED_SET_CHILD2(paned, content_paned);
-
+
GtkWidget *right_content_box = ui_gtk_vbox_new(0);
PANED_SET_CHILD1(content_paned, content_box);
PANED_SET_CHILD2(content_paned, right_content_box);
-
+
g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content_paned);
g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", content_box);
g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_content_box);
} else {
PANED_SET_CHILD2(paned, content_box);
}
-
+
BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
} else {
BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
}
-
+
#endif
-
+
// window content
// the content has a (TODO: not yet) configurable frame
// TODO: really? why
GtkWidget *frame = gtk_frame_new(NULL);
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
-
+
// content vbox
GtkWidget *content_box = ui_gtk_vbox_new(0);
gtk_container_add(GTK_CONTAINER(frame), content_box);
*/
UiContainer *container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX);
uic_object_push_container(obj, container);
-
+
nwindows++;
return obj;
}
UIEXPORT void ui_splitview_window_set_visible(UiObject *obj, int pane, UiBool visible) {
GtkWidget *panel = NULL;
+ CxList *backup_items = NULL;
if(pane == 0) {
panel = g_object_get_data(G_OBJECT(obj->widget), "ui_left_panel_top");
+ backup_items = g_object_get_data(G_OBJECT(obj->widget), "ui_toolbar_center_backup");
} else if(pane == 1) {
panel = g_object_get_data(G_OBJECT(obj->widget), "ui_right_panel_top");
+ // TODO: backup toolbar items
}
if(panel == NULL) {
}
gtk_widget_set_visible(panel, visible);
+
+ if(backup_items) {
+ CxIterator i = cxListIterator(backup_items);
+ cx_foreach(GtkWidget*, w, i) {
+ ui_set_visible(w, !visible);
+ }
+ }
}
#ifdef UI_LIBADWAITA