UiGeneric *img;
} AttachmentModel;
+typedef struct AttachmentWindow {
+ UiObject *obj;
+
+ Resource *resource;
+ UiList *attachments;
+
+ UiGeneric *image;
+} AttachmentWindow;
+
void application_init();
void application_startup(UiEvent *event, void *data);
#include "attachment.h"
+#include "note.h"
+
+#include <cx/printf.h>
+
Attachment* attachment_create(
const CxAllocator *a,
int64_t parent,
return attachment;
}
-void attachment_create_ui_model(UiContext *ctx, Attachment *attachment) {
+void attachment_create_ui_model(UiContext *ctx, Attachment *attachment, Resource *resource) {
if(attachment->ui) {
return;
}
model->img = ui_generic_new(ctx, NULL);
}
model->ctx = ctx;
+
+ model->parent_note = resource;
}
void attachment_set_image(Attachment *attachment, void *img) {
Attachment *attachment = elm;
if(attachment->type == NOTE_ATTACHMENT_IMAGE) {
- UIWIDGET imgviewer = ui_imageviewer(obj, .value = attachment->ui->img, .scrollarea = FALSE, .autoscale = TRUE);
+ UIWIDGET imgviewer = ui_imageviewer(obj,
+ .value = attachment->ui->img,
+ .scrollarea = FALSE,
+ .autoscale = TRUE,
+ .onbuttonpress = action_attachment_clicked,
+ .onbuttonpressdata = attachment);
ui_widget_set_size(imgviewer, 100, 80);
}
}
+
+void action_attachment_clicked(UiEvent *event, void *userdata) {
+ Attachment *attachment = userdata;
+
+ UiObject *attachment_window = attachment_window_create(attachment->ui->parent_note, attachment);
+ ui_show(attachment_window);
+}
+
+
+UiObject* attachment_window_create(Resource *resource, Attachment *selected_attachment) {
+ cxmutstr title = cx_asprintf("%s - attachments", note_get_title(resource));
+ UiObject *obj = ui_simple_window(title.ptr, NULL);
+ free(title.ptr);
+
+ AttachmentWindow *wdata = attachment_window_create_data(obj, resource);
+ obj->window = wdata;
+
+ ui_headerbar(obj) {
+ ui_headerbar_start(obj) {
+ ui_button(obj, .icon = UI_ICON_GO_BACK);
+ ui_button(obj, .icon = UI_ICON_GO_FORWARD);
+ }
+ }
+
+ ui_imageviewer(obj, .value = wdata->image, .autoscale = TRUE, .scrollarea = TRUE, .useradjustable = TRUE, .fill = UI_ON);
+
+
+ return obj;
+}
+
+static void update_attachments(AttachmentWindow *wdata, NoteModel *model) {
+ void *elm = ui_list_first(model->attachments);
+ while(elm) {
+ ui_list_append(wdata->attachments, elm);
+ elm = ui_list_next(model->attachments);
+ }
+}
+
+AttachmentWindow* attachment_window_create_data(UiObject *obj, Resource *resource) {
+ AttachmentWindow *wdata = ui_malloc(obj->ctx, sizeof(AttachmentWindow));
+ memset(wdata, 0, sizeof(AttachmentWindow));
+
+ wdata->obj = obj;
+ wdata->resource = resource;
+ wdata->attachments = ui_list_new(obj->ctx, NULL);
+ wdata->image = ui_generic_new(obj->ctx, NULL);
+
+ update_attachments(wdata, resource->model);
+
+ return wdata;
+}
AttachmentType type,
const char *name);
-void attachment_create_ui_model(UiContext *ctx, Attachment *attachment);
+void attachment_create_ui_model(UiContext *ctx, Attachment *attachment, Resource *resource);
void attachment_set_image(Attachment *attachment, void *img);
*/
void attachment_item(UiObject *obj, int index, void *elm, void *userdata);
+void action_attachment_clicked(UiEvent *event, void *userdata);
+
+UiObject* attachment_window_create(Resource *resource, Attachment *selected_attachment);
+
+AttachmentWindow* attachment_window_create_data(UiObject *obj, Resource *resource);
#ifdef __cplusplus
}
// create attachment
Attachment *attachment = attachment_create(model->note_allocator, note->resource_id, NOTE_ATTACHMENT_IMAGE, util_resource_name(attachment_path));
- attachment_create_ui_model(model->ctx, attachment);
+ attachment_create_ui_model(model->ctx, attachment, note);
g_object_ref(pixbuf);
attachment_set_image(attachment, pixbuf);
UiImageViewer *imgviewer = malloc(sizeof(UiImageViewer));
memset(imgviewer, 0, sizeof(UiImageViewer));
+ imgviewer->obj = obj;
+ imgviewer->onbuttonpress = args.onbuttonpress;
+ imgviewer->onbuttonpressdata = args.onbuttonpressdata;
+ imgviewer->onbuttonrelease = args.onbuttonrelease;
+ imgviewer->onbuttonreleasedata = args.onbuttonreleasedata;
if(args.image_padding > 0) {
imgviewer->padding_left = args.image_padding;
imgviewer->padding_right = args.image_padding;
g_signal_connect(drag, "drag-update", G_CALLBACK(ui_imageviewer_drag_update_cb), imgviewer);
gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(drag));
+ GtkGesture *click = gtk_gesture_click_new();
+ g_signal_connect(click, "pressed", G_CALLBACK(ui_imageviewer_pressed_cb), imgviewer);
+ g_signal_connect(click, "released", G_CALLBACK(ui_imageviewer_released_cb), imgviewer);
+ gtk_widget_add_controller(GTK_WIDGET(drawingarea), GTK_EVENT_CONTROLLER(click));
+
#elif GTK_MAJOR_VERSION == 3
g_signal_connect(
drawingarea,
}
void ui_imageviewer_drag_begin_cb(
- GtkGestureDrag* self,
+ GtkGestureDrag *self,
gdouble start_x,
gdouble start_y,
gpointer userdata)
}
void ui_imageviewer_drag_update_cb(
- GtkGestureDrag* self,
+ GtkGestureDrag *self,
gdouble x,
gdouble y,
gpointer userdata)
}
}
+static void imgviewer_button_event(
+ GtkGestureClick *gesture,
+ UiImageViewer *imgviewer,
+ ui_callback callback,
+ void *userdata)
+{
+ UiEvent event;
+ event.obj = imgviewer->obj;
+ event.window = event.obj->window;
+ event.document = event.obj->ctx->document;
+ event.eventdata = NULL;
+ event.intval = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(gesture));
+ event.set = 0;
+ callback(&event, userdata);
+}
+
+void ui_imageviewer_pressed_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata)
+{
+ UiImageViewer *imgviewer = userdata;
+ if(imgviewer->onbuttonpress) {
+ imgviewer_button_event(self, imgviewer, imgviewer->onbuttonpress, imgviewer->onbuttonpressdata);
+ }
+}
+
+void ui_imageviewer_released_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata)
+{
+ UiImageViewer *imgviewer = userdata;
+ if(imgviewer->onbuttonrelease) {
+ imgviewer_button_event(self, imgviewer, imgviewer->onbuttonrelease, imgviewer->onbuttonreleasedata);
+ }
+}
+
#else
gboolean ui_imageviewer_scroll_event(
GdkEventScroll event,
gpointer userdata)
{
- printf("scroll event\n");
+ // TODO
return FALSE;
}
GdkEventButton event,
gpointer userdata)
{
- printf("button pressed\n");
+ // TODO
+ return FALSE;
}
gboolean ui_imageviewer_button_release_event(
GdkEventButton event,
gpointer userdata)
{
- printf("button released\n");
+ // TODO
+ return FALSE;
}
#endif
#endif
typedef struct UiImageViewer {
+ UiObject *obj;
GtkWidget *widget;
UiVar *var;
int padding_left;
UiBool isautoscaled;
double user_scale;
double scale;
+
+ ui_callback onbuttonpress;
+ void *onbuttonpressdata;
+ ui_callback onbuttonrelease;
+ void *onbuttonreleasedata;
} UiImageViewer;
void ui_cairo_draw_image(UiImageViewer *imgviewer, cairo_t *cr, int width, int height);
gdouble y,
gpointer userdata);
+void ui_imageviewer_pressed_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata);
+
+void ui_imageviewer_released_cb(
+ GtkGestureClick *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ gpointer userdata);
+
#else
gboolean ui_imageviewer_scroll_event(
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2015 Olaf Wintermann. All rights reserved.
+ * 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:
SOURCES += button.cpp
SOURCES += label.cpp
SOURCES += graphics.cpp
+SOURCES += widget.cpp
HEADERS += toolkit.h
HEADERS += window.h
HEADERS += button.h
HEADERS += label.h
HEADERS += graphics.h
+HEADERS += widget.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.
+ */
+
+#include "widget.h"
+
+#include "container.h"
+#include "../common/context.h"
+
+UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs args) {
+ UIWIDGET widget = create_widget(obj, args, userdata);
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UI_APPLY_LAYOUT(ctn->layout, args);
+ ctn->add(widget, false);
+ return widget;
+}
+
+UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) {
+ QFrame *separator = new QFrame();
+ separator->setFrameShape(QFrame::HLine);
+ separator->setFrameShadow(QFrame::Sunken);
+
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+ UI_APPLY_LAYOUT(ctn->layout, (*args));
+
+ ctn->add(separator, false);
+
+ return separator;
+}
+
+void ui_widget_set_size(UIWIDGET w, int width, int height) {
+ w->resize(width >= 0 ? width : w->width(), height >= 0 ? w->height());
+}
+
+void ui_widget_redraw(UIWIDGET w) {
+ w->repaint();
+}
--- /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.
+ */
+
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include "toolkit.h"
+
+#include "../ui/widget.h"
+
+
+#endif /* WIDGET_H */
+
UiGeneric *value;
const char *varname;
UiMenuBuilder *contextmenu;
+
+ ui_callback onbuttonpress;
+ void *onbuttonpressdata;
+ ui_callback onbuttonrelease;
+ void *onbuttonreleasedata;
} UiImageViewerArgs;
#define ui_imageviewer(obj, ...) ui_imageviewer_create(obj, (UiImageViewerArgs){ __VA_ARGS__ } )