ui/gtk/text.c

branch
newapi
changeset 310
4918f9132552
parent 298
c5e207d01ff2
child 313
b679cc6059ab
equal deleted inserted replaced
309:02c95df91de1 310:4918f9132552
689 *ret_nelm = n; 689 *ret_nelm = n;
690 690
691 return elms; 691 return elms;
692 } 692 }
693 693
694
695 #if GTK_MAJOR_VERSION >= 4
696
697 UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) {
698 // TODO
699 return NULL;
700 }
701
702 #else
703
704 static gboolean path_textfield_btn_pressed(GtkWidget *widget, GdkEventButton *event, UiPathTextField *pathtf) {
705 gtk_box_pack_start(GTK_BOX(pathtf->hbox), pathtf->entry, TRUE, TRUE, 0);
706 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox);
707 pathtf->buttonbox = NULL;
708
709 gtk_widget_show(pathtf->entry);
710 gtk_widget_grab_focus(pathtf->entry);
711
712 return TRUE;
713 }
714
715 static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { 694 static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) {
716 for(int i=0;i<nelm;i++) { 695 for(int i=0;i<nelm;i++) {
717 free(elms[i].name); 696 free(elms[i].name);
718 free(elms[i].path); 697 free(elms[i].path);
719 } 698 }
720 free(elms); 699 free(elms);
721 } 700 }
722 701
723 static void ui_path_textfield_destroy(GtkWidget *object, UiPathTextField *pathtf) { 702 static void ui_path_textfield_destroy(GtkWidget *object, UiPathTextField *pathtf) {
724 free(pathtf->current_path); 703 free(pathtf->hbox);
725 g_object_unref(pathtf->entry); 704 g_object_unref(pathtf->entry);
726 free(pathtf); 705 free(pathtf);
727 } 706 }
728 707
708 void ui_path_button_clicked(GtkWidget *widget, UiEventData *event) {
709 UiPathElm *elm = event->customdata;
710 cxmutstr path = cx_strdup(cx_strn(elm->path, elm->path_len));
711 UiEvent evt;
712 evt.obj = event->obj;
713 evt.window = evt.obj->window;
714 evt.document = evt.obj->ctx->document;
715 evt.eventdata = elm->path;
716 evt.intval = event->value;
717 event->callback(&evt, event->userdata);
718 free(path.ptr);
719 }
720
721 int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path) {
722 size_t full_path_len = strlen(full_path);
723 if(full_path_len == 0) {
724 return 1;
725 }
726
727 size_t nelm = 0;
728 UiPathElm* path_elm = pathtf->getpathelm(full_path, full_path_len, &nelm, pathtf->getpathelmdata);
729 if (!path_elm) {
730 return 1;
731 }
732
733 free(pathtf->current_path);
734 pathtf->current_path = strdup(full_path);
735
736 ui_pathelm_destroy(pathtf->current_pathelms, pathtf->current_nelm);
737 pathtf->current_pathelms = path_elm;
738 pathtf->current_nelm = nelm;
739
740 return ui_pathtextfield_update_widget(pathtf);
741 }
742
743 static GtkWidget* ui_path_elm_button(UiPathTextField *pathtf, UiPathElm *elm, int i) {
744 cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len));
745 GtkWidget *button = gtk_button_new_with_label(name.ptr);
746 free(name.ptr);
747
748 if(pathtf->onactivate) {
749 UiEventData *eventdata = malloc(sizeof(UiEventData));
750 eventdata->callback = pathtf->onactivate;
751 eventdata->userdata = pathtf->onactivatedata;
752 eventdata->obj = pathtf->obj;
753 eventdata->customdata = elm;
754 eventdata->value = i;
755
756 g_signal_connect(
757 button,
758 "clicked",
759 G_CALLBACK(ui_path_button_clicked),
760 eventdata);
761
762 g_signal_connect(
763 button,
764 "destroy",
765 G_CALLBACK(ui_destroy_userdata),
766 eventdata);
767 }
768
769 return button;
770 }
771
729 static void ui_path_textfield_activate(GtkWidget *entry, UiPathTextField *pathtf) { 772 static void ui_path_textfield_activate(GtkWidget *entry, UiPathTextField *pathtf) {
730 const gchar *text = gtk_entry_get_text(GTK_ENTRY(pathtf->entry)); 773 const gchar *text = ENTRY_GET_TEXT(pathtf->entry);
731 if(strlen(text) == 0) { 774 if(strlen(text) == 0) {
732 return; 775 return;
733 } 776 }
734 777
735 UiObject *obj = pathtf->obj; 778 UiObject *obj = pathtf->obj;
747 evt.intval = -1; 790 evt.intval = -1;
748 pathtf->onactivate(&evt, pathtf->onactivatedata); 791 pathtf->onactivate(&evt, pathtf->onactivatedata);
749 } 792 }
750 } 793 }
751 794
752 static gboolean ui_path_textfield_key_press(GtkWidget *self, GdkEventKey *event, UiPathTextField *pathtf) { 795 #if GTK_MAJOR_VERSION >= 4
753 if (event->keyval == GDK_KEY_Escape) { 796
754 // reset GtkEntry value 797 static void pathbar_show_hbox(GtkWidget *widget, UiPathTextField *pathtf) {
755 gtk_entry_set_text(GTK_ENTRY(self), pathtf->current_path); 798 if(pathtf->current_path) {
756 const gchar *text = gtk_entry_get_text(GTK_ENTRY(self)); 799 gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox);
757 ui_pathtextfield_update(pathtf, text); 800 ENTRY_SET_TEXT(pathtf->entry, pathtf->current_path);
758 return TRUE; 801 }
802 }
803
804 static gboolean ui_path_textfield_key_controller(
805 GtkEventControllerKey* self,
806 guint keyval,
807 guint keycode,
808 GdkModifierType state,
809 UiPathTextField *pathtf)
810 {
811 if(keyval == GDK_KEY_Escape) {
812 pathbar_show_hbox(NULL, pathtf);
759 } 813 }
760 return FALSE; 814 return FALSE;
761 }
762
763 static GtkWidget* create_path_button_box() {
764 GtkWidget *bb = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
765 gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_EXPAND); // linked style
766 gtk_box_set_homogeneous(GTK_BOX(bb), FALSE);
767 gtk_box_set_spacing(GTK_BOX(bb), 0);
768 return bb;
769 } 815 }
770 816
771 UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { 817 UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) {
772 UiObject* current = uic_current_obj(obj); 818 UiObject* current = uic_current_obj(obj);
773 819
788 if(!pathtf->getpathelm) { 834 if(!pathtf->getpathelm) {
789 pathtf->getpathelm = default_pathelm_func; 835 pathtf->getpathelm = default_pathelm_func;
790 pathtf->getpathelmdata = NULL; 836 pathtf->getpathelmdata = NULL;
791 } 837 }
792 838
839 pathtf->stack = gtk_stack_new();
840 gtk_widget_set_name(pathtf->stack, "path-textfield-box");
841
842 UI_APPLY_LAYOUT1(current, args);
843 current->container->add(current->container, pathtf->stack, FALSE);
844
845 pathtf->entry_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
846 pathtf->entry = gtk_entry_new();
847 gtk_box_append(GTK_BOX(pathtf->entry_box), pathtf->entry);
848 gtk_widget_set_hexpand(pathtf->entry, TRUE);
849
850 GtkWidget *cancel_button = gtk_button_new_from_icon_name("window-close-symbolic");
851 gtk_widget_add_css_class(cancel_button, "flat");
852 gtk_widget_add_css_class(cancel_button, "pathbar-extra-button");
853 gtk_box_append(GTK_BOX(pathtf->entry_box), cancel_button);
854 g_signal_connect(
855 cancel_button,
856 "clicked",
857 G_CALLBACK(pathbar_show_hbox),
858 pathtf);
859
860 gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->entry_box);
861 g_object_ref(pathtf->entry); // for compatibility with older pathbar version
862 g_signal_connect(
863 pathtf->entry,
864 "activate",
865 G_CALLBACK(ui_path_textfield_activate),
866 pathtf);
867
868 GtkEventController *entry_cancel = gtk_event_controller_key_new();
869 gtk_widget_add_controller(pathtf->entry, entry_cancel);
870 g_signal_connect(entry_cancel, "key-pressed", G_CALLBACK(ui_path_textfield_key_controller), pathtf);
871
872 gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box);
873
874
875 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING);
876 if (var) {
877 UiString* value = (UiString*)var->value;
878 value->obj = pathtf;
879 value->get = ui_path_textfield_get;
880 value->set = ui_path_textfield_set;
881
882 if(value->value.ptr) {
883 char *str = strdup(value->value.ptr);
884 ui_string_set(value, str);
885 free(str);
886 }
887 }
888
889 return pathtf->stack;
890 }
891
892 static void pathbar_pressed(
893 GtkGestureClick* self,
894 gint n_press,
895 gdouble x,
896 gdouble y,
897 UiPathTextField *pathtf)
898 {
899 gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box);
900 gtk_widget_grab_focus(pathtf->entry);
901 }
902
903 int ui_pathtextfield_update_widget(UiPathTextField* pathtf) {
904 // recreate button hbox
905 if(pathtf->hbox) {
906 gtk_stack_remove(GTK_STACK(pathtf->stack), pathtf->hbox);
907 }
908 pathtf->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
909 gtk_box_set_homogeneous(GTK_BOX(pathtf->hbox), FALSE);
910 gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->hbox);
911 gtk_widget_set_name(pathtf->hbox, "pathbar");
912
913 // add buttons for path elements
914 for (int i=0;i<pathtf->current_nelm;i++) {
915 UiPathElm *elm = &pathtf->current_pathelms[i];
916
917 GtkWidget *button = ui_path_elm_button(pathtf, elm, i);
918 gtk_widget_add_css_class(button, "flat");
919
920 gtk_box_append(GTK_BOX(pathtf->hbox), button);
921
922 if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), CX_STR("/"))) {
923 gtk_box_append(GTK_BOX(pathtf->hbox), gtk_label_new("/"));
924 }
925 }
926 gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox);
927
928 // create a widget for receiving button press events
929 GtkWidget *event_area = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
930 GtkGesture *handler = gtk_gesture_click_new();
931 gtk_widget_add_controller(event_area, GTK_EVENT_CONTROLLER(handler));
932 g_signal_connect(
933 handler,
934 "pressed",
935 G_CALLBACK(pathbar_pressed),
936 pathtf);
937 gtk_widget_set_hexpand(event_area, TRUE);
938 gtk_widget_set_vexpand(event_area, TRUE);
939 gtk_box_append(GTK_BOX(pathtf->hbox), event_area);
940
941 return 0;
942 }
943
944 #else
945
946 static gboolean path_textfield_btn_pressed(GtkWidget *widget, GdkEventButton *event, UiPathTextField *pathtf) {
947 gtk_box_pack_start(GTK_BOX(pathtf->hbox), pathtf->entry, TRUE, TRUE, 0);
948 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox);
949 pathtf->buttonbox = NULL;
950
951 gtk_widget_show(pathtf->entry);
952 gtk_widget_grab_focus(pathtf->entry);
953
954 return TRUE;
955 }
956
957 static gboolean ui_path_textfield_key_press(GtkWidget *self, GdkEventKey *event, UiPathTextField *pathtf) {
958 if (event->keyval == GDK_KEY_Escape) {
959 // reset GtkEntry value
960 gtk_entry_set_text(GTK_ENTRY(self), pathtf->current_path);
961 const gchar *text = gtk_entry_get_text(GTK_ENTRY(self));
962 ui_pathtextfield_update(pathtf, text);
963 return TRUE;
964 }
965 return FALSE;
966 }
967
968 static GtkWidget* create_path_button_box() {
969 GtkWidget *bb = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
970 gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_EXPAND); // linked style
971 gtk_box_set_homogeneous(GTK_BOX(bb), FALSE);
972 gtk_box_set_spacing(GTK_BOX(bb), 0);
973 return bb;
974 }
975
976 UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) {
977 UiObject* current = uic_current_obj(obj);
978
979 UiPathTextField *pathtf = malloc(sizeof(UiPathTextField));
980 memset(pathtf, 0, sizeof(UiPathTextField));
981 pathtf->obj = obj;
982 pathtf->getpathelm = args.getpathelm;
983 pathtf->getpathelmdata = args.getpathelmdata;
984 pathtf->onactivate = args.onactivate;
985 pathtf->onactivatedata = args.onactivatedata;
986 pathtf->ondragcomplete = args.ondragcomplete;
987 pathtf->ondragcompletedata = args.ondragcompletedata;
988 pathtf->ondragstart = args.ondragstart;
989 pathtf->ondragstartdata = args.ondragstartdata;
990 pathtf->ondrop = args.ondrop;
991 pathtf->ondropdata = args.ondropsdata;
992
993 if(!pathtf->getpathelm) {
994 pathtf->getpathelm = default_pathelm_func;
995 pathtf->getpathelmdata = NULL;
996 }
997
793 // top level container for the path textfield is a GtkEventBox 998 // top level container for the path textfield is a GtkEventBox
794 // the event box is needed to handle background button presses 999 // the event box is needed to handle background button presses
795 GtkWidget *eventbox = gtk_event_box_new(); 1000 GtkWidget *eventbox = gtk_event_box_new();
796 g_signal_connect( 1001 g_signal_connect(
797 eventbox, 1002 eventbox,
844 } 1049 }
845 1050
846 return hbox; 1051 return hbox;
847 } 1052 }
848 1053
849 void ui_path_button_clicked(GtkWidget *widget, UiEventData *event) { 1054 int ui_pathtextfield_update_widget(UiPathTextField* pathtf) {
850 UiPathElm *elm = event->customdata;
851 cxmutstr path = cx_strdup(cx_strn(elm->path, elm->path_len));
852 UiEvent evt;
853 evt.obj = event->obj;
854 evt.window = evt.obj->window;
855 evt.document = evt.obj->ctx->document;
856 evt.eventdata = elm->path;
857 evt.intval = event->value;
858 event->callback(&evt, event->userdata);
859 free(path.ptr);
860 }
861
862 int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path) {
863 size_t full_path_len = strlen(full_path);
864 if(full_path_len == 0) {
865 return 1;
866 }
867
868 size_t nelm = 0;
869 UiPathElm* path_elm = pathtf->getpathelm(full_path, full_path_len, &nelm, pathtf->getpathelmdata);
870 if (!path_elm) {
871 return 1;
872 }
873
874 free(pathtf->current_path);
875 pathtf->current_path = strdup(full_path);
876
877 ui_pathelm_destroy(pathtf->current_pathelms, pathtf->current_nelm);
878 pathtf->current_pathelms = path_elm;
879 pathtf->current_nelm = nelm;
880
881 GtkWidget *buttonbox = create_path_button_box(); 1055 GtkWidget *buttonbox = create_path_button_box();
882 1056
883 // switch from entry to buttonbox or remove current buttonbox 1057 // switch from entry to buttonbox or remove current buttonbox
884 if(pathtf->buttonbox) { 1058 if(pathtf->buttonbox) {
885 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox); 1059 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox);
887 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->entry); 1061 gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->entry);
888 } 1062 }
889 gtk_box_pack_start(GTK_BOX(pathtf->hbox), buttonbox, FALSE, FALSE, 0); 1063 gtk_box_pack_start(GTK_BOX(pathtf->hbox), buttonbox, FALSE, FALSE, 0);
890 pathtf->buttonbox = buttonbox; 1064 pathtf->buttonbox = buttonbox;
891 1065
892 for (int i=0;i<nelm;i++) { 1066 for (int i=0;i<pathtf->current_nelm;i++) {
893 UiPathElm *elm = &path_elm[i]; 1067 UiPathElm *elm = &pathtf->current_pathelms[i];
894 1068 GtkWidget *button = ui_path_elm_button(pathtf, elm, i);
895 cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len));
896 GtkWidget *button = gtk_button_new_with_label(name.ptr);
897 free(name.ptr);
898
899 if(pathtf->onactivate) {
900 UiEventData *eventdata = malloc(sizeof(UiEventData));
901 eventdata->callback = pathtf->onactivate;
902 eventdata->userdata = pathtf->onactivatedata;
903 eventdata->obj = pathtf->obj;
904 eventdata->customdata = elm;
905 eventdata->value = i;
906
907 g_signal_connect(
908 button,
909 "clicked",
910 G_CALLBACK(ui_path_button_clicked),
911 eventdata);
912
913 g_signal_connect(
914 button,
915 "destroy",
916 G_CALLBACK(ui_destroy_userdata),
917 eventdata);
918 }
919
920 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0); 1069 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
921 } 1070 }
922 1071
923 gtk_widget_show_all(buttonbox); 1072 gtk_widget_show_all(buttonbox);
924 1073
925 return 0; 1074 return 0;
926 } 1075 }
1076
1077 #endif
927 1078
928 char* ui_path_textfield_get(UiString *str) { 1079 char* ui_path_textfield_get(UiString *str) {
929 if(str->value.ptr) { 1080 if(str->value.ptr) {
930 str->value.free(str->value.ptr); 1081 str->value.free(str->value.ptr);
931 } 1082 }
932 UiPathTextField *tf = str->obj; 1083 UiPathTextField *tf = str->obj;
933 str->value.ptr = g_strdup(gtk_entry_get_text(GTK_ENTRY(tf->entry))); 1084 str->value.ptr = g_strdup(ENTRY_GET_TEXT(tf->entry));
934 str->value.free = (ui_freefunc)g_free; 1085 str->value.free = (ui_freefunc)g_free;
935 return str->value.ptr; 1086 return str->value.ptr;
936 } 1087 }
937 1088
938 void ui_path_textfield_set(UiString *str, const char *value) { 1089 void ui_path_textfield_set(UiString *str, const char *value) {
939 UiPathTextField *tf = str->obj; 1090 UiPathTextField *tf = str->obj;
940 gtk_entry_set_text(GTK_ENTRY(tf->entry), value); 1091 ENTRY_SET_TEXT(tf->entry, value);
941 ui_pathtextfield_update(tf, value); 1092 ui_pathtextfield_update(tf, value);
942 if(str->value.ptr) { 1093 if(str->value.ptr) {
943 str->value.free(str->value.ptr); 1094 str->value.free(str->value.ptr);
944 str->value.ptr = NULL; 1095 str->value.ptr = NULL;
945 str->value.free = NULL; 1096 str->value.free = NULL;
946 } 1097 }
947 } 1098 }
948
949 #endif

mercurial