cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a);
UiBool editor_set_style(UiText *text, const char *style, UiBool enabled);
+void editor_set_paragraph_style(UiText *text, const char *style);
#ifdef __cplusplus
}
}
}
-static void editor_set_cursor_cb(
- GtkTextBuffer *buffer,
- const GtkTextIter *location,
- GtkTextMark *mark,
- NoteEditor *editor)
-{
- const char *mark_name = gtk_text_mark_get_name(mark);
- if(!mark_name || strcmp(mark_name, "insert")) {
- return;
- }
-
+// update the paragraph style dropdown list to the style of the current
+// cursor position
+static void update_cursor_paragraph_style(NoteEditor *editor, GtkTextBuffer *buffer) {
GtkTextIter pos;
// get current tags
// when the buffer has a selection, get the tags from the selection start
}
}
+static void editor_set_cursor_cb(
+ GtkTextBuffer *buffer,
+ const GtkTextIter *location,
+ GtkTextMark *mark,
+ NoteEditor *editor)
+{
+ const char *mark_name = gtk_text_mark_get_name(mark);
+ if(!mark_name || strcmp(mark_name, "insert")) {
+ return;
+ }
+
+ update_cursor_paragraph_style(editor, buffer);
+}
+
void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos) {
GSList *tags = gtk_text_iter_get_tags(pos);
while(tags) {
gtk_text_tag_table_add(table, tag);
tag = gtk_text_tag_new(EDITOR_STYLE_CODE_BLOCK);
- g_object_set(tag, "family", "Monospace", "paragraph-background", "#080808", NULL);
+ g_object_set(tag, "family", "Monospace", "paragraph-background", "#efefef", NULL);
g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_codeblock);
gtk_text_tag_table_add(table, tag);
return TRUE;
}
+
+static void remove_paragraph_styles(GtkTextBuffer *buffer, GtkTextIter *begin, GtkTextIter *end) {
+ char *para[] = {
+ EDITOR_STYLE_PARAGRAPH,
+ EDITOR_STYLE_HEADING1,
+ EDITOR_STYLE_HEADING2,
+ EDITOR_STYLE_HEADING3,
+ EDITOR_STYLE_HEADING4,
+ EDITOR_STYLE_HEADING5,
+ EDITOR_STYLE_HEADING6,
+ EDITOR_STYLE_QUOTE,
+ EDITOR_STYLE_CODE_BLOCK,
+ NULL
+ };
+
+ for(int i=0;para[i] != NULL;i++) {
+ gtk_text_buffer_remove_tag_by_name(buffer, para[i], begin, end);
+ }
+}
+
+void editor_set_paragraph_style(UiText *text, const char *style) {
+ GtkTextBuffer *buffer = text->data1;
+ NoteEditor *editor = g_object_get_data(text->obj, "editor");
+
+ // currently changing the selection's paragraph style is not supported
+ if(gtk_text_buffer_get_has_selection(buffer)) {
+ update_cursor_paragraph_style(editor, buffer);
+ return;
+ }
+
+ GtkTextMark *cursor = gtk_text_buffer_get_insert(buffer);
+ if(!cursor) {
+ fprintf(stderr, "Error: no insert mark\n");
+ return;
+ }
+ GtkTextIter pos;
+ gtk_text_buffer_get_iter_at_mark(buffer, &pos, cursor);
+ GtkTextIter begin = pos;
+ GtkTextIter end = pos;
+ gtk_text_iter_set_line_offset(&begin, 0);
+ gtk_text_iter_forward_to_line_end(&end);
+
+ remove_paragraph_styles(buffer, &begin, &end);
+ gtk_text_buffer_apply_tag_by_name(buffer, style, &begin, &end);
+}
#include "store.h"
#include "editor.h"
+static TextNoteParagraphStyles paragraph_styles[] = {
+ { "Paragraph", EDITOR_STYLE_PARAGRAPH },
+ { "Code", EDITOR_STYLE_CODE_BLOCK },
+ { "Blockquote", EDITOR_STYLE_QUOTE },
+ { "Heading 1", EDITOR_STYLE_HEADING1 },
+ { "Heading 2", EDITOR_STYLE_HEADING2 },
+ { "Heading 3", EDITOR_STYLE_HEADING3 },
+ { "Heading 4", EDITOR_STYLE_HEADING4 },
+ { "Heading 5", EDITOR_STYLE_HEADING5 },
+ { "Heading 6", EDITOR_STYLE_HEADING6 }
+};
+
+static size_t num_paragraph_styles = 9;
+
NoteModel *notemodel_current(UiObject *obj) {
MainWindow *win = obj->window;
if(win->current_notebook && win->current_notebook->current_note) {
model->textnote_para = ui_list_new(model->ctx, "note_textnote_para");
// currently only one type of textnote exists, therefore the
// paragraph types are static
- ui_list_append(model->textnote_para, "Paragraph");
- ui_list_append(model->textnote_para, "Code");
- ui_list_append(model->textnote_para, "Blockquote");
- ui_list_append(model->textnote_para, "Heading 1");
- ui_list_append(model->textnote_para, "Heading 2");
- ui_list_append(model->textnote_para, "Heading 3");
- ui_list_append(model->textnote_para, "Heading 4");
- ui_list_append(model->textnote_para, "Heading 5");
- ui_list_append(model->textnote_para, "Heading 6");
+ for(int i=0;i<num_paragraph_styles;i++) {
+ ui_list_append(model->textnote_para, (void*)paragraph_styles[i].label);
+ }
model->textnote_strong = ui_int_new(model->ctx, "note_textnote_strong");
model->textnote_emphasis = ui_int_new(model->ctx, "note_textnote_emphasis");
ui_set(note->textnote_code, style->code);
}
+void note_set_paragraph_type(NoteModel *note, int style) {
+ if(style < 0 || style >= num_paragraph_styles) {
+ fprintf(stderr, "Invalid paragraph style %d\n", style);
+ return;
+ }
+
+ const char *style_name = paragraph_styles[style].style;
+ editor_set_paragraph_style(note->text, style_name);
+}
+
void note_text_style_set_strong(NoteModel *note, UiBool enabled) {
if(!editor_set_style(note->text, EDITOR_STYLE_STRONG, enabled)) {
ui_set(note->textnote_strong, !enabled);
extern "C" {
#endif
+typedef struct TextNoteParagraphStyles {
+ const char *label;
+ const char *style;
+} TextNoteParagraphStyles;
+
NoteModel *notemodel_current(UiObject *obj);
NoteModel* notemodel_create(const CxAllocator *note_allocator);
void note_update_current_style(NoteModel *note, MDActiveStyles *style);
+void note_set_paragraph_type(NoteModel *note, int style);
void note_text_style_set_strong(NoteModel *note, UiBool enabled);
void note_text_style_set_emphasis(NoteModel *note, UiBool enabled);
void note_text_style_set_underline(NoteModel *note, UiBool enabled);
ui_newline(obj);
}
ui_hbox(obj, .style_class = "note_toolbar", .margin = 10, .spacing = 4, .fill = UI_OFF) {
- ui_combobox(obj, .varname = "note_textnote_para");
+ ui_combobox(obj, .varname = "note_textnote_para", .onactivate = action_textnote_paragraph);
ui_togglebutton(obj, .icon = "format-text-bold", .varname = "note_textnote_strong", .onchange = action_textnote_style_strong);
ui_togglebutton(obj, .icon = "format-text-italic", .varname = "note_textnote_emphasis", .onchange = action_textnote_style_emphasis);
ui_togglebutton(obj, .icon = "format-text-underline", .varname = "note_textnote_underline", .onchange = action_textnote_style_underline);
note_text_style_set_code(notebook->current_note->model, event->intval);
}
}
+
+void action_textnote_paragraph(UiEvent *event, void *userdata) {
+ if(event->set) {
+ return;
+ }
+ MainWindow *window = event->window;
+ NotebookModel *notebook = window->current_notebook;
+ if(notebook && notebook->current_note && notebook->current_note->model) {
+ note_set_paragraph_type(notebook->current_note->model, event->intval);
+ }
+}
void action_note_selected(UiEvent *event, void *userdata);
void action_note_activated(UiEvent *event, void *userdata);
+void action_textnote_paragraph(UiEvent *event, void *userdata);
void action_textnote_style_strong(UiEvent *event, void *userdata);
void action_textnote_style_emphasis(UiEvent *event, void *userdata);
void action_textnote_style_underline(UiEvent *event, void *userdata);
if(self.orientation == NSUserInterfaceLayoutOrientationHorizontal) {
[view.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES;
if(!fill) {
- [view.widthAnchor constraintEqualToConstant:view.intrinsicContentSize.width].active = YES;
+ NSSize isize = view.intrinsicContentSize;
+ [view.widthAnchor constraintEqualToConstant:isize.width].active = YES;
}
} else {
[view.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
if(!fill) {
- [view.heightAnchor constraintEqualToConstant:view.intrinsicContentSize.height].active = YES;
+ NSSize isize = view.intrinsicContentSize;
+ NSRect frame = view.frame;
+ CGFloat height = isize.height > 0 ? isize.height : frame.size.height;
+ if(height == 0) {
+ printf("debug");
+ }
+ if(height > 0) {
+ [view.heightAnchor constraintEqualToConstant:height].active = YES;
+ }
}
}
COCOAOBJ += window.o
COCOAOBJ += Container.o
COCOAOBJ += button.o
+COCOAOBJ += text.o
TOOLKITOBJS += $(COCOAOBJ:%=$(COCOA_OBJPRE)%)
TOOLKITSOURCE += $(COCOAOBJ:%.o=cocoa/%.m)
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "toolkit.h"
+
+#import "../ui/text.h"
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "text.h"
+#import "EventData.h"
+#import "Container.h"
+#import <objc/runtime.h>
+
+UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) {
+ //NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:(NSRect){ 0, 0, 40, 40}];
+ NSTextView *textview = [[NSTextView alloc] initWithFrame:(NSRect){ 0, 0, 40, 40}];
+ //scrollview.documentView = textview;
+
+ UiLayout layout = UI_INIT_LAYOUT(args);
+ ui_container_add(obj, textview, &layout, TRUE);
+
+ return (__bridge void*)textview;
+}
cxMempoolSetDestructor(destr, (cx_destructor_func)observer_destructor);
}
-
static int ui_set_op = 0;
void ui_setop_enable(int set) {
int ui_get_setop(void) {
return ui_set_op;
}
-
return view;
}
+void ui_listview_select(UIWIDGET listview, int index) {
+ GtkSelectionModel *model = gtk_list_view_get_model(GTK_LIST_VIEW(listview));
+ gtk_selection_model_select_item(model, index, TRUE);
+}
+
+void ui_combobox_select(UIWIDGET dropdown, int index) {
+ gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index);
+}
+
UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
event.window = event.obj->window;
event.intval = view->selection.count;
event.eventdata = &view->selection;
+ event.set = ui_get_setop();
if(cb) {
cb(&event, cbdata);
}
event.window = event.obj->window;
event.intval = index;
event.eventdata = eventdata->data;
+ event.set = ui_get_setop();
view->onactivate(&event, view->onactivatedata);
}
}
event.window = event.obj->window;
event.intval = view->selection.count;
event.eventdata = &view->selection;
+ event.set = ui_get_setop();
view->onactivate(&event, view->onactivatedata);
}
}
}
void ui_listview_setselection2(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
UiListView *view = list->obj;
UiListSelection newselection;
newselection.count = view->selection.count;
gtk_selection_model_select_item(view->selectionmodel, selection.rows[i], FALSE);
}
}
+ ui_setop_enable(FALSE);
}
UiListSelection ui_combobox_getselection(UiList *list) {
}
void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
UiListView *view = list->obj;
if(selection.count > 0) {
gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), selection.rows[0]);
} else {
gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), GTK_INVALID_LIST_POSITION);
}
+ ui_setop_enable(FALSE);
}
#else
return scroll_area;
}
+void ui_listview_select(UIWIDGET listview, int index) {
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview));
+ GtkTreePath *path = gtk_tree_path_new_from_indicesv(&index, 1);
+ gtk_tree_selection_select_path(sel, path);
+ //g_object_unref(path);
+}
+
+void ui_combobox_select(UIWIDGET dropdown, int index) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index);
+}
+
UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
}
void ui_listview_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
UiListView *view = list->obj;
GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget));
GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count);
gtk_tree_selection_select_path(sel, path);
//g_object_unref(path);
+ ui_setop_enable(FALSE);
}
event->callback = f;
event->value = 0;
event->customdata = uicbox;
+ event.set = ui_get_setop();
g_signal_connect(
combobox,
event.document = event.obj->ctx->document;
event.eventdata = eventdata;
event.intval = index;
+ event.set = ui_get_setop();
e->callback(&event, e->userdata);
}
}
void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
UiListView *combobox = list->obj;
if(selection.count > 0) {
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]);
}
+ ui_setop_enable(FALSE);
}
e.document = event->obj->ctx->document;
e.eventdata = &selection;
e.intval = selection.count > 0 ? selection.rows[0] : -1;
+ e.set = ui_get_setop();
event->activate(&e, event->activatedata);
if(selection.count > 0) {
e.document = event->obj->ctx->document;
e.eventdata = &selection;
e.intval = selection.count > 0 ? selection.rows[0] : -1;
+ e.set = ui_get_setop();
event->selection(&e, event->selectiondata);
if(selection.count > 0) {
event.document = event.obj->ctx->document;
event.eventdata = dnd;
event.intval = 0;
+ event.set = ui_get_setop();
listview->ondragstart(&event, listview->ondragstartdata);
}
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval = 0;
+ event.set = ui_get_setop();
listview->ondragcomplete(&event, listview->ondragcompletedata);
}
}
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval = 0;
+ event.set = ui_get_setop();
listview->ondrop(&event, listview->ondropdata);
}
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval = 0;
+ event.set = ui_get_setop();
listview->ondragstart(&event, listview->ondragstartdata);
}
}
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval = 0;
+ event.set = ui_get_setop();
listview->ondragcomplete(&event, listview->ondragcompletedata);
}
}
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval = 0;
+ event.set = ui_get_setop();
listview->ondrop(&event, listview->ondropdata);
}
}
event.document = event.obj->ctx->document;
event.eventdata = &eventdata;
event.intval = data->value0;
+ event.set = ui_get_setop();
if(data->callback) {
data->callback(&event, data->userdata);
e.window = event->obj->window;
e.document = event->obj->ctx->document;
e.intval = event->value;
+ e.set = 0;
event->callback(&e, event->userdata);
}
e.document = e.obj->ctx->document;
e.eventdata = NULL;
e.intval = XmToggleButtonGetState(w);
+ e.set = ui_get_setop();
if(event->callback) {
event->callback(&e, event->userdata);
e.document = e.obj->ctx->document;
e.eventdata = value;
e.intval = v;
+ e.set = ui_get_setop();
if(event->callback) {
event->callback(&e, event->userdata);
}
void ui_listview_setselection(UiList *list, UiListSelection selection) {
+ ui_setop_enable(TRUE);
UiListView *listview = list->obj;
XmListDeselectAllItems(listview->widget);
for(int i=0;i<selection.count;i++) {
XmListSelectPos(listview->widget, selection.rows[i]+1, False);
}
+ ui_setop_enable(FALSE);
}
void* ui_strmodel_getvalue(void *elm, int column) {
UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs args);
UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs args);
+UIEXPORT void ui_listview_select(UIWIDGET listview, int index);
+UIEXPORT void ui_combobox_select(UIWIDGET dropdown, int index);
+
UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args);