Skip to content

Commit 99e3135

Browse files
committed
make pin widget stackable
1 parent 63d43c0 commit 99e3135

File tree

4 files changed

+118
-85
lines changed

4 files changed

+118
-85
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- better row drop
1515
- cancel row drag with Esc
1616
- better row drag animation
17+
- menu pins stack
1718

1819
## 9.0.11 2025/07/21
1920

TODO

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
- is the magick menu caching correctly? we see a lot of forking for each
2+
action
3+
4+
eg. Annotate runs the annotation 9 times as the row evaluates
5+
6+
system should cache on the input file pointer plus the cmd string
7+
8+
removed NOCACHE from system, still runs 9 times :(
9+
10+
- magick menu should use magick7
111

212
- get judder with rotate and images smaller than the window
313

src/toolkitgroupview.c

Lines changed: 91 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -215,22 +215,35 @@ toolkitgroupview_dispose(GObject *object)
215215
Toolkitgroupview *kitgview = TOOLKITGROUPVIEW(object);
216216

217217
gtk_widget_dispose_template(GTK_WIDGET(kitgview), TOOLKITGROUPVIEW_TYPE);
218-
g_slist_free_full(g_steal_pointer(&kitgview->page_names), g_free);
218+
for (int i = 0; i < MAX_PAGE_DEPTH; i++)
219+
VIPS_FREE(kitgview->page_names[i]);
219220

220221
G_OBJECT_CLASS(toolkitgroupview_parent_class)->dispose(object);
221222
}
222223

223-
static GSList *
224-
toolkitgroupview_parse_path(char *path)
224+
static void
225+
toolkitgroupview_set_path(Toolkitgroupview *kitgview, char *path)
225226
{
226-
GSList *page_names = NULL;
227+
for (int i = 0; i < MAX_PAGE_DEPTH; i++) {
228+
VIPS_FREE(kitgview->page_names[i]);
229+
kitgview->pinned[i] = FALSE;
230+
}
231+
kitgview->n_pages = 1;
232+
kitgview->page_names[0] = g_strdup("root");
233+
234+
if (path) {
235+
char *p;
236+
char *q;
227237

228-
char *p;
229-
char *q;
230-
for (p = path; (q = vips_break_token(p, ">")); p = q)
231-
page_names = g_slist_append(page_names, g_strdup(p));
238+
kitgview->n_pages = 0;
239+
for (p = path; (q = vips_break_token(p, ">")); p = q) {
240+
VIPS_SETSTR(kitgview->page_names[kitgview->n_pages], g_strdup(p));
232241

233-
return page_names;
242+
if (kitgview->n_pages >= MAX_PAGE_DEPTH - 1)
243+
return;
244+
kitgview->n_pages += 1;
245+
}
246+
}
234247
}
235248

236249
static void
@@ -250,8 +263,7 @@ toolkitgroupview_set_property(GObject *object,
250263
case PROP_PATH:
251264
path = g_strdup(g_value_get_string(value));
252265
if (strlen(path) > 0) {
253-
g_slist_free_full(g_steal_pointer(&kitgview->page_names), g_free);
254-
kitgview->page_names = toolkitgroupview_parse_path(path);
266+
toolkitgroupview_set_path(kitgview, path);
255267
kitgview->search_mode = FALSE;
256268
vobject_refresh_queue(VOBJECT(kitgview));
257269
}
@@ -264,8 +276,7 @@ toolkitgroupview_set_property(GObject *object,
264276
vobject_refresh_queue(VOBJECT(kitgview));
265277
gtk_editable_set_text(GTK_EDITABLE(kitgview->search_entry),
266278
search_text);
267-
gtk_string_filter_set_search(kitgview->filter,
268-
search_text);
279+
gtk_string_filter_set_search(kitgview->filter, search_text);
269280
}
270281
break;
271282

@@ -276,16 +287,14 @@ toolkitgroupview_set_property(GObject *object,
276287
}
277288

278289
static char *
279-
toolkitgroupview_print_path(GSList *page_names)
290+
toolkitgroupview_print_path(Toolkitgroupview *kitgview)
280291
{
281292
GString *path = g_string_new("");
282293

283-
for (GSList *p = page_names; p; p = p->next) {
284-
const char *name = (const char *) p->data;
285-
286-
g_string_append(path, name);
287-
if (p->next)
294+
for (int i = 0; i < kitgview->n_pages; i++) {
295+
if (i > 0)
288296
g_string_append(path, ">");
297+
g_string_append(path, kitgview->page_names[i]);
289298
}
290299

291300
return g_string_free_and_steal(path);
@@ -306,8 +315,7 @@ toolkitgroupview_get_property(GObject *object,
306315
if (kitgview->search_mode)
307316
g_value_set_string(value, "");
308317
else
309-
g_value_take_string(value,
310-
toolkitgroupview_print_path(kitgview->page_names));
318+
g_value_take_string(value, toolkitgroupview_print_path(kitgview));
311319
break;
312320

313321
case PROP_SEARCH:
@@ -425,8 +433,27 @@ toolkitgroupview_build_node(Toolkitgroupview *kitgview, Node *parent)
425433
return G_LIST_MODEL(store);
426434
}
427435

436+
// go back one, if we can
437+
static void
438+
toolkitgroupview_browse_back(Toolkitgroupview *kitgview)
439+
{
440+
if (kitgview->n_pages > 1) {
441+
GtkWidget *last_page =
442+
gtk_stack_get_visible_child(GTK_STACK(kitgview->stack));
443+
444+
// display the second-last page
445+
gtk_stack_set_visible_child_name(GTK_STACK(kitgview->stack),
446+
kitgview->page_names[kitgview->n_pages - 2]);
447+
448+
// delete the last page
449+
gtk_stack_remove(GTK_STACK(kitgview->stack), last_page);
450+
451+
kitgview->n_pages -= 1;
452+
}
453+
}
454+
428455
static GtkWidget *
429-
toolkitgroupview_build_browse_page(Toolkitgroupview *kitgview, Node *parent);
456+
toolkitgroupview_add_browse_page(Toolkitgroupview *kitgview, Node *parent);
430457

431458
static void
432459
toolkitgroupview_browse_clicked(GtkWidget *button,
@@ -438,41 +465,27 @@ toolkitgroupview_browse_clicked(GtkWidget *button,
438465
GtkWidget *left = gtk_widget_get_first_child(box);
439466

440467
// click on "go back?"
441-
if (gtk_widget_is_visible(left)) {
442-
// remove the last item from the page_names list ... we know we are
443-
// at least one deep in the menu
444-
g_assert(kitgview->page_names->next);
445-
char *last_name = (char *) g_slist_last(kitgview->page_names)->data;
446-
kitgview->page_names = g_slist_remove(kitgview->page_names, last_name);
447-
g_free(last_name);
448-
449-
// get the (new) last name
450-
const char *name =
451-
(const char *) g_slist_last(kitgview->page_names)->data;
452-
GtkWidget *last_page =
453-
gtk_stack_get_visible_child(GTK_STACK(kitgview->stack));
454-
455-
gtk_stack_set_visible_child_name(GTK_STACK(kitgview->stack), name);
456-
gtk_stack_remove(GTK_STACK(kitgview->stack), last_page);
457-
kitgview->pin = NULL;
458-
}
468+
if (gtk_widget_is_visible(left))
469+
toolkitgroupview_browse_back(kitgview);
459470
else {
460471
if (node->kit ||
461-
(node->toolitem && node->toolitem->is_pullright)) {
462-
// go right ... make sure we clear the pin widget first
463-
if (kitgview->pin)
464-
gtk_check_button_set_active(GTK_CHECK_BUTTON(kitgview->pin),
465-
FALSE);
466-
kitgview->pin = NULL;
467-
toolkitgroupview_build_browse_page(kitgview, node);
468-
}
472+
(node->toolitem && node->toolitem->is_pullright))
473+
toolkitgroupview_add_browse_page(kitgview, node);
469474

470475
// activate after moving to the new page so listeners can get the new
471476
// page name
472477
toolkitgroupview_activate(kitgview, node->toolitem, node->tool);
473478
}
474479
}
475480

481+
static void
482+
toolkitgroupview_pin_toggled(GtkCheckButton *self, gpointer user_data)
483+
{
484+
Toolkitgroupview *kitgview = TOOLKITGROUPVIEW(user_data);
485+
486+
kitgview->pinned[kitgview->n_pages - 1] ^= 1;
487+
}
488+
476489
static void
477490
toolkitgroupview_setup_browse_item(GtkListItemFactory *factory,
478491
GtkListItem *item, Toolkitgroupview *kitgview)
@@ -507,6 +520,8 @@ toolkitgroupview_setup_browse_item(GtkListItemFactory *factory,
507520

508521
GtkWidget *pin = gtk_check_button_new();
509522
set_tooltip(pin, "Pin menu in place");
523+
g_signal_connect(pin, "toggled",
524+
G_CALLBACK(toolkitgroupview_pin_toggled), kitgview);
510525
gtk_box_append(GTK_BOX(enclosing), pin);
511526

512527
gtk_list_item_set_child(item, enclosing);
@@ -541,8 +556,7 @@ toolkitgroupview_bind_browse_item(GtkListItemFactory *factory,
541556
gtk_label_set_xalign(GTK_LABEL(label), 0.5);
542557
g_object_set_qdata(G_OBJECT(button), node_quark, parent);
543558

544-
// remember the widget so we can test and set the state
545-
kitgview->pin = pin;
559+
// note the pin widget on the page
546560
}
547561
else {
548562
gtk_label_set_xalign(GTK_LABEL(label), 0.0);
@@ -583,20 +597,22 @@ toolkitgroupview_build_browse_list_view(Toolkitgroupview *kitgview, Node *this)
583597
}
584598

585599
static GtkWidget *
586-
toolkitgroupview_build_browse_page(Toolkitgroupview *kitgview, Node *this)
600+
toolkitgroupview_add_browse_page(Toolkitgroupview *kitgview, Node *this)
587601
{
588602
const char *name = node_get_name(this);
589603

590604
#ifdef DEBUG
591-
printf("toolkitgroupview_build_browse_page: adding page %s\n", name);
605+
printf("toolkitgroupview_add_browse_page: adding page %s\n", name);
592606
#endif /*DEBUG*/
593607

594608
GtkWidget *scrolled_window = gtk_scrolled_window_new();
595609
// no scrollbars ... they obstruct useful widgets
596610
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
597611
GTK_POLICY_EXTERNAL, GTK_POLICY_EXTERNAL);
598612
gtk_stack_add_named(GTK_STACK(kitgview->stack), scrolled_window, name);
599-
kitgview->page_names = g_slist_append(kitgview->page_names, g_strdup(name));
613+
kitgview->page_names[kitgview->n_pages] = g_strdup(name);
614+
kitgview->pinned[kitgview->n_pages] = FALSE;
615+
kitgview->n_pages += 1;
600616

601617
GtkWidget *list_view =
602618
toolkitgroupview_build_browse_list_view(kitgview, this);
@@ -618,17 +634,23 @@ toolkitgroupview_rebuild_browse(Toolkitgroupview *kitgview)
618634
g_assert(!kitgview->list_view);
619635
g_assert(!kitgview->filter);
620636

621-
// take a copy of the old pages_names list and reset to the root
622-
GSList *old_page_names = kitgview->page_names;
623-
kitgview->page_names = g_slist_append(NULL, g_strdup("root"));
624-
625637
// don't animate the browser rebuild process
626638
gboolean should_animate =
627639
widget_should_animate(GTK_WIDGET(kitgview->stack));
628640
g_object_set(gtk_widget_get_settings(GTK_WIDGET(kitgview->stack)),
629641
"gtk-enable-animations", FALSE,
630642
NULL);
631643

644+
// steal the old pages_names list and reset to the root
645+
char *old_page_names[MAX_PAGE_DEPTH];
646+
int old_n_pages = kitgview->n_pages;
647+
for (int i = 0; i < old_n_pages; i++) {
648+
old_page_names[i] = kitgview->page_names[i];
649+
kitgview->page_names[i] = NULL;
650+
}
651+
652+
toolkitgroupview_set_path(kitgview, NULL);
653+
632654
// build and fill the root page in the root scrolled window
633655
kitgview->list_view =
634656
toolkitgroupview_build_browse_list_view(kitgview, NULL);
@@ -639,20 +661,18 @@ toolkitgroupview_rebuild_browse(Toolkitgroupview *kitgview)
639661
G_LIST_MODEL(gtk_list_view_get_model(
640662
GTK_LIST_VIEW(kitgview->list_view)));
641663

642-
for (GSList *p = old_page_names->next; p; p = p->next) {
643-
char *name = (char *) p->data;
644-
664+
for (int i = 0; i < old_n_pages; i++) {
645665
/* Is there an item on the page we've just made with the next name on
646666
* the page list?
647667
*/
648668
Node *this = NULL;
649-
for (int i = 0; i < g_list_model_get_n_items(list_model); i++) {
650-
Node *item = g_list_model_get_item(list_model, i);
669+
for (int j = 0; j < g_list_model_get_n_items(list_model); j++) {
670+
Node *item = g_list_model_get_item(list_model, j);
651671
const char *item_name = node_get_name(item);
652672

653673
// name can be NULL for eg separators
654674
if (item_name &&
655-
g_str_equal(item_name, name)) {
675+
g_str_equal(item_name, old_page_names[i])) {
656676
this = item;
657677
break;
658678
}
@@ -661,12 +681,13 @@ toolkitgroupview_rebuild_browse(Toolkitgroupview *kitgview)
661681
break;
662682

663683
GtkWidget *list_view =
664-
toolkitgroupview_build_browse_page(kitgview, this);
684+
toolkitgroupview_add_browse_page(kitgview, this);
665685
list_model = G_LIST_MODEL(gtk_list_view_get_model(
666686
GTK_LIST_VIEW(list_view)));
667687
}
668688

669-
g_slist_free_full(g_steal_pointer(&old_page_names), g_free);
689+
for (int i = 0; i < old_n_pages; i++)
690+
VIPS_FREE(old_page_names[i]);
670691

671692
g_object_set(gtk_widget_get_settings(GTK_WIDGET(kitgview->stack)),
672693
"gtk-enable-animations", should_animate,
@@ -826,8 +847,7 @@ toolkitgroupview_rebuild_list(Toolkitgroupview *kitgview)
826847
gtk_scrolled_window_set_child(
827848
GTK_SCROLLED_WINDOW(kitgview->scrolled_window), kitgview->list_view);
828849

829-
// truncate the page_names list, since we're a flat view
830-
g_slist_free_full(g_steal_pointer(&kitgview->page_names->next), g_free);
850+
kitgview->n_pages = 1;
831851
}
832852

833853
static void
@@ -839,8 +859,6 @@ toolkitgroupview_refresh(vObject *vobject)
839859
printf("toolkitgroupview_refresh:\n");
840860
#endif /*DEBUG*/
841861

842-
kitgview->pin = NULL;
843-
844862
// remove all stack pages except the first
845863
GtkWidget *stack = kitgview->stack;
846864
GtkWidget *root_page = gtk_widget_get_first_child(stack);
@@ -849,8 +867,6 @@ toolkitgroupview_refresh(vObject *vobject)
849867

850868
while ((child = gtk_widget_get_next_sibling(root_page)))
851869
gtk_stack_remove(GTK_STACK(stack), child);
852-
853-
kitgview->pin = NULL;
854870
}
855871
gtk_stack_set_visible_child(GTK_STACK(kitgview->stack), root_page);
856872

@@ -1018,8 +1034,7 @@ toolkitgroupview_init(Toolkitgroupview *kitgview)
10181034
{
10191035
gtk_widget_init_template(GTK_WIDGET(kitgview));
10201036

1021-
kitgview->page_names =
1022-
g_slist_append(kitgview->page_names, g_strdup("root"));
1037+
toolkitgroupview_set_path(kitgview, NULL);
10231038

10241039
GtkEntryBuffer *buffer =
10251040
gtk_entry_get_buffer(GTK_ENTRY(kitgview->search_entry));
@@ -1038,12 +1053,7 @@ toolkitgroupview_new(void)
10381053
void
10391054
toolkitgroupview_home(Toolkitgroupview *kitgview)
10401055
{
1041-
if (!kitgview->pin ||
1042-
!gtk_check_button_get_active(GTK_CHECK_BUTTON(kitgview->pin))) {
1043-
// back to just the "root" page
1044-
if (kitgview->page_names)
1045-
g_slist_free_full(g_steal_pointer(&kitgview->page_names->next),
1046-
g_free);
1047-
vobject_refresh_queue(VOBJECT(kitgview));
1048-
}
1056+
while(kitgview->n_pages > 1 &&
1057+
!kitgview->pinned[kitgview->n_pages - 1])
1058+
toolkitgroupview_browse_back(kitgview);
10491059
}

0 commit comments

Comments
 (0)