ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit b0338320849b8d570511491a73dee2d75aff5fd9
parent d0099e84bdff7d265d14fc3ae226b0881ba98ed6
Author: lumidify <nobody@lumidify.org>
Date:   Sun, 17 Jan 2021 21:25:32 +0100

Add very broken scrollbar support to box

Diffstat:
M.ltk/theme.ini | 2+-
Mbox.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mbutton.c | 6+++---
Mgrid.c | 6+++---
Mlabel.c | 6+++---
Mltk.h | 6+++++-
Mltkd.c | 40+++++++++++++++++++++++++++-------------
Mscrollbar.c | 71+++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mscrollbar.h | 1+
Mtext.h | 2+-
Mtext_line.c | 11++++++-----
Mtext_pango.c | 2+-
Mtext_stb.c | 2+-
13 files changed, 156 insertions(+), 87 deletions(-)

diff --git a/.ltk/theme.ini b/.ltk/theme.ini @@ -23,7 +23,7 @@ text_color = #FFFFFF pad = 5 [scrollbar] -size = 5 +size = 15 bg = #000000 bg_disabled = #555555 fg = #113355 diff --git a/box.c b/box.c @@ -34,7 +34,7 @@ #include "scrollbar.h" #include "box.h" -static void ltk_box_draw(ltk_box *box); +static void ltk_box_draw(ltk_box *box, ltk_rect clip); static ltk_box *ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient); static void ltk_box_destroy(ltk_box *box, int shallow); static void ltk_recalculate_box(ltk_box *box); @@ -72,12 +72,16 @@ static int ltk_box_cmd_create( char **errstr); static void -ltk_box_draw(ltk_box *box) { +ltk_box_draw(ltk_box *box, ltk_rect clip) { ltk_widget *ptr; + ltk_rect real_clip = ltk_rect_intersect(box->widget.rect, clip); for (size_t i = 0; i < box->num_widgets; i++) { ptr = box->widgets[i]; - ptr->draw(ptr); + /* FIXME: Maybe continue immediately if widget is + obviously outside of clipping rect */ + ptr->draw(ptr, real_clip); } + box->sc->widget.draw(box->sc, real_clip); } static ltk_box * @@ -95,7 +99,7 @@ ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient) { box->widget.child_size_change = &ltk_box_child_size_change; box->widget.remove_child = &ltk_box_remove; - box->sc = NULL; + box->sc = ltk_scrollbar_create(window, orient); box->widgets = NULL; box->num_alloc = 0; box->num_widgets = 0; @@ -116,38 +120,42 @@ ltk_box_destroy(ltk_box *box, int shallow) { free(box->widgets); ltk_remove_widget(box->widget.window, box->widget.id); free(box->widget.id); + box->sc->widget.destroy(box->sc, 0); free(box); } -/* FIXME: Need some sort of "visible rect" so widgets don't draw outside */ /* FIXME: Make this function name more consistent */ +/* FIXME: The widget positions are set with the old scrollbar->cur_pos, before the + virtual_size is set - this can cause problems when a widget changes its size + (in the scrolled direction) when resized. */ static void ltk_recalculate_box(ltk_box *box) { ltk_widget *ptr; + ltk_rect *sc_rect = &box->sc->widget.rect; int cur_pos = 0; for (size_t i = 0; i < box->num_widgets; i++) { ptr = box->widgets[i]; if (box->orient == LTK_HORIZONTAL) { - ptr->rect.x = cur_pos; + ptr->rect.x = cur_pos - box->sc->cur_pos; if (ptr->sticky & LTK_STICKY_TOP && ptr->sticky & LTK_STICKY_BOTTOM) - ptr->rect.h = box->widget.rect.h; + ptr->rect.h = box->widget.rect.h - sc_rect->h; if (ptr->sticky & LTK_STICKY_TOP) ptr->rect.y = box->widget.rect.y; else if (ptr->sticky & LTK_STICKY_BOTTOM) - ptr->rect.y = box->widget.rect.y + box->widget.rect.h - ptr->rect.h; + ptr->rect.y = box->widget.rect.y + box->widget.rect.h - ptr->rect.h - sc_rect->h; else ptr->rect.y = box->widget.rect.y + (box->widget.rect.h - ptr->rect.h) / 2; if (ptr->resize) ptr->resize(ptr); cur_pos += ptr->rect.w; } else { - ptr->rect.y = cur_pos; + ptr->rect.y = cur_pos - box->sc->cur_pos; if (ptr->sticky & LTK_STICKY_LEFT && ptr->sticky & LTK_STICKY_RIGHT) - ptr->rect.w = box->widget.rect.w; + ptr->rect.w = box->widget.rect.w - sc_rect->w; if (ptr->sticky & LTK_STICKY_LEFT) ptr->rect.x = box->widget.rect.x; else if (ptr->sticky & LTK_STICKY_RIGHT) - ptr->rect.x = box->widget.rect.x + box->widget.rect.w - ptr->rect.w; + ptr->rect.x = box->widget.rect.x + box->widget.rect.w - ptr->rect.w - sc_rect->w; else ptr->rect.x = box->widget.rect.x + (box->widget.rect.w - ptr->rect.w) / 2; if (ptr->resize) @@ -155,6 +163,14 @@ ltk_recalculate_box(ltk_box *box) { cur_pos += ptr->rect.h; } } + ltk_scrollbar_set_virtual_size(box->sc, cur_pos); + if (box->orient == LTK_HORIZONTAL) { + sc_rect->y = box->widget.rect.y + box->widget.rect.h - sc_rect->h; + sc_rect->w = box->widget.rect.w; + } else { + sc_rect->x = box->widget.rect.x + box->widget.rect.w - sc_rect->w; + sc_rect->h = box->widget.rect.h; + } } /* FIXME: This entire resizing thing is a bit weird. For instance, if a label @@ -179,11 +195,13 @@ ltk_box_child_size_change(ltk_box *box, ltk_widget *widget) { settings, but there'd probably be some catch as well. */ widget->rect.w = widget->ideal_w; widget->rect.h = widget->ideal_h; - if (box->orient == LTK_HORIZONTAL && widget->ideal_h > box->widget.ideal_h) { - box->widget.ideal_h = widget->ideal_h; + int sc_w = box->sc->widget.rect.w; + int sc_h = box->sc->widget.rect.h; + if (box->orient == LTK_HORIZONTAL && widget->ideal_h + sc_h > box->widget.ideal_h) { + box->widget.ideal_h = widget->ideal_h + sc_h; size_changed = 1; - } else if (box->orient == LTK_VERTICAL && widget->ideal_w > box->widget.ideal_h) { - box->widget.ideal_w = widget->ideal_w; + } else if (box->orient == LTK_VERTICAL && widget->ideal_w + sc_w > box->widget.ideal_h) { + box->widget.ideal_w = widget->ideal_w + sc_w; size_changed = 1; } @@ -208,11 +226,19 @@ ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, unsigned short box->widgets = new; } + int sc_w = box->sc->widget.rect.w; + int sc_h = box->sc->widget.rect.h; + box->widgets[box->num_widgets++] = widget; - if (box->orient == LTK_HORIZONTAL) + if (box->orient == LTK_HORIZONTAL) { box->widget.ideal_w += widget->ideal_w; - else + if (widget->ideal_h + sc_h > box->widget.ideal_h) + box->widget.ideal_h = widget->ideal_h + sc_h; + } else { box->widget.ideal_h += widget->ideal_h; + if (widget->ideal_w + sc_w > box->widget.ideal_w) + box->widget.ideal_w = widget->ideal_w + sc_w; + } widget->parent = box; widget->sticky = sticky; ltk_box_child_size_change(box, widget); @@ -223,6 +249,8 @@ ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, unsigned short static int ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_box *box, char **errstr) { + int sc_w = box->sc->widget.rect.w; + int sc_h = box->sc->widget.rect.h; if (widget->parent != box) { *errstr = "Widget isn't contained in given box.\n"; return 1; @@ -237,19 +265,19 @@ ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_box *box, char **errs ltk_window_invalidate_rect(window, box->widget.rect); /* search for new ideal width/height */ /* FIXME: make this all a bit nicer and break the lines better */ - if (box->orient == LTK_HORIZONTAL && widget->ideal_h == box->widget.ideal_h) { + if (box->orient == LTK_HORIZONTAL && widget->ideal_h + sc_h == box->widget.ideal_h) { box->widget.ideal_h = 0; for (size_t j = 0; j < box->num_widgets; j++) { - if (box->widgets[j]->ideal_h > box->widget.ideal_h) - box->widget.ideal_h = box->widgets[j]->ideal_h; + if (box->widgets[j]->ideal_h + sc_h > box->widget.ideal_h) + box->widget.ideal_h = box->widgets[j]->ideal_h + sc_h; } if (box->widget.parent && box->widget.parent->resize) box->widget.parent->resize(box->widget.parent); - } else if (box->orient == LTK_VERTICAL && widget->ideal_w == box->widget.ideal_w) { + } else if (box->orient == LTK_VERTICAL && widget->ideal_w + sc_w == box->widget.ideal_w) { box->widget.ideal_w = 0; for (size_t j = 0; j < box->num_widgets; j++) { - if (box->widgets[j]->ideal_w > box->widget.ideal_w) - box->widget.ideal_w = box->widgets[j]->ideal_w; + if (box->widgets[j]->ideal_w + sc_w > box->widget.ideal_w) + box->widget.ideal_w = box->widgets[j]->ideal_w + sc_w; } if (box->widget.parent && box->widget.parent->resize) box->widget.parent->resize(box->widget.parent); @@ -266,11 +294,16 @@ static void ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XEvent)) { int pos, start, size; ltk_widget *widget; + int old_sc_pos = box->sc->cur_pos; - /* - if (ltk_collide_rect(box->sc.widget.rect, event.xbutton.x, event.xbutton.y)) + if (ltk_collide_rect(box->sc->widget.rect, event.xbutton.x, event.xbutton.y)) { handler(box->sc, event); - */ + if (old_sc_pos != box->sc->cur_pos) { + ltk_recalculate_box(box); + ltk_window_invalidate_rect(box->widget.window, box->widget.rect); + } + return; + } /* FIXME: When scrolling is implemented, check only the currently visible items */ for (size_t i = 0; i < box->num_widgets; i++) { @@ -297,9 +330,6 @@ ltk_box_mouse_release(ltk_box *box, XEvent event) { even if not actually hovered over! */ static void ltk_box_motion_notify(ltk_box *box, XEvent event) { - short pressed = (event.xmotion.state & Button1Mask) == Button1Mask; - if (pressed) - return; ltk_box_mouse_event(box, event, &ltk_widget_motion_notify_event); } diff --git a/button.c b/button.c @@ -36,7 +36,7 @@ #include "text.h" #include "button.h" -static void ltk_button_draw(ltk_button *button); +static void ltk_button_draw(ltk_button *button, ltk_rect clip); static void ltk_button_mouse_release(ltk_button *button, XEvent event); static ltk_button *ltk_button_create(ltk_window *window, const char *id, const char *text); @@ -125,7 +125,7 @@ ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value) } static void -ltk_button_draw(ltk_button *button) { +ltk_button_draw(ltk_button *button, ltk_rect clip) { ltk_window *window = button->widget.window; ltk_rect rect = button->widget.rect; int bw = theme.border_width; @@ -166,7 +166,7 @@ ltk_button_draw(ltk_button *button) { ltk_text_line_get_size(button->tl, &text_w, &text_h); int text_x = rect.x + (rect.w - text_w) / 2; int text_y = rect.y + (rect.h - text_h) / 2; - ltk_text_line_draw(button->tl, window->gc, text_x, text_y); + ltk_text_line_draw(button->tl, window->gc, text_x, text_y, clip); } static void diff --git a/grid.c b/grid.c @@ -42,7 +42,7 @@ static void ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight); static void ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight); -static void ltk_grid_draw(ltk_grid *grid); +static void ltk_grid_draw(ltk_grid *grid, ltk_rect clip); static ltk_grid *ltk_grid_create(ltk_window *window, const char *id, int rows, int columns); static void ltk_grid_destroy(ltk_grid *grid, int shallow); @@ -96,13 +96,13 @@ ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight) { } static void -ltk_grid_draw(ltk_grid *grid) { +ltk_grid_draw(ltk_grid *grid, ltk_rect clip) { int i; for (i = 0; i < grid->rows * grid->columns; i++) { if (!grid->widget_grid[i]) continue; ltk_widget *ptr = grid->widget_grid[i]; - ptr->draw(ptr); + ptr->draw(ptr, clip); } } diff --git a/label.c b/label.c @@ -36,7 +36,7 @@ #include "text.h" #include "label.h" -static void ltk_label_draw(ltk_label *label); +static void ltk_label_draw(ltk_label *label, ltk_rect clip); static ltk_label *ltk_label_create(ltk_window *window, const char *id, const char *text); static void ltk_label_destroy(ltk_label *label, int shallow); @@ -66,7 +66,7 @@ ltk_label_ini_handler(ltk_window *window, const char *prop, const char *value) { } static void -ltk_label_draw(ltk_label *label) { +ltk_label_draw(ltk_label *label, ltk_rect clip) { ltk_window *window = label->widget.window; ltk_rect rect = label->widget.rect; @@ -74,7 +74,7 @@ ltk_label_draw(ltk_label *label) { ltk_text_line_get_size(label->tl, &text_w, &text_h); int text_x = rect.x + (rect.w - text_w) / 2; int text_y = rect.y + (rect.h - text_h) / 2; - ltk_text_line_draw(label->tl, window->gc, text_x, text_y); + ltk_text_line_draw(label->tl, window->gc, text_x, text_y, clip); } static ltk_label * diff --git a/ltk.h b/ltk.h @@ -59,6 +59,8 @@ typedef enum { } ltk_widget_state; typedef enum { + /* for e.g. scrollbar, which can't be directly accessed by users */ + LTK_UNKNOWN, LTK_GRID, LTK_BUTTON, LTK_DRAW, @@ -88,7 +90,7 @@ typedef struct ltk_widget { void (*mouse_enter) (void *, XEvent); void (*resize) (void *); - void (*draw) (void *); + void (*draw) (void *, ltk_rect); void (*change_state) (void *); void (*destroy) (void *, int); @@ -147,6 +149,8 @@ typedef struct ltk_window { struct ltk_event_queue *last_event; } ltk_window; +ltk_rect ltk_rect_intersect(ltk_rect r1, ltk_rect r2); +ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2); void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect); void ltk_warn(const char *format, ...); void ltk_fatal(const char *format, ...); diff --git a/ltkd.c b/ltkd.c @@ -1,3 +1,4 @@ +/* FIXME: PROPERLY CLEANUP ALL THEMES */ /* FIXME: error checking in tokenizer (is this necessary?) */ /* FIXME: parsing doesn't work properly with bs? */ /* FIXME: strip whitespace at end of lines in socket format */ @@ -101,7 +102,6 @@ static void ltk_redraw_window(ltk_window *window); static void ltk_window_other_event(ltk_window *window, XEvent event); static void ltk_handle_event(ltk_window *window, XEvent event); static void ltk_load_theme(ltk_window *window, const char *path); -static ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2); static int read_sock(struct ltk_sock_info *sock); static int push_token(struct token_list *tl, char *token); static int read_sock(struct ltk_sock_info *sock); @@ -110,7 +110,6 @@ static int queue_sock_write(struct ltk_sock_info *sock, const char *str, int len static int tokenize_command(struct ltk_sock_info *sock); static int ltk_set_root_widget_cmd(ltk_window *window, char **tokens, int num_tokens, char **errstr); static void process_commands(ltk_window *window, struct ltk_sock_info *sock); -static ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2); static int add_client(int fd); static int listen_sock(const char *sock_path); static int accept_sock(int listenfd); @@ -406,15 +405,26 @@ ltk_set_root_widget_cmd( return 0; } -static ltk_rect +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +ltk_rect +ltk_rect_intersect(ltk_rect r1, ltk_rect r2) { + ltk_rect i; + i.x = MAX(r1.x, r2.x); + i.y = MAX(r1.y, r2.y); + i.w = MIN(r1.x + r1.w, r2.x + r2.w) - i.x; + i.h = MIN(r1.y + r1.h, r2.y + r2.h) - i.y; + return i; +} + +ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2) { ltk_rect u; - u.x = r1.x < r2.x ? r1.x : r2.x; - u.y = r1.y < r2.y ? r1.y : r2.y; - int x2 = r1.x + r1.w < r2.x + r2.w ? r2.x + r2.w : r1.x + r1.w; - int y2 = r1.y + r1.h < r2.y + r2.h ? r2.y + r2.h : r1.y + r1.h; - u.w = x2 - u.x; - u.h = y2 - u.y; + u.x = MIN(r1.x, r2.x); + u.y = MIN(r1.y, r2.y); + u.w = MAX(r1.x + r1.w, r2.x + r2.w) - u.x; + u.h = MAX(r1.y + r1.h, r2.y + r2.h) - u.y; return u; } @@ -530,9 +540,9 @@ ltk_redraw_window(ltk_window *window) { window->dirty_rect.h -= window->dirty_rect.y + window->dirty_rect.h - window->rect.h; XClearArea(window->dpy, window->xwindow, window->dirty_rect.x, window->dirty_rect.y, window->dirty_rect.w, window->dirty_rect.h, False); if (!window->root_widget) return; - /* FIXME: actually respect the dirty rect... */ ptr = window->root_widget; - ptr->draw(ptr); + if (ptr) + ptr->draw(ptr, window->dirty_rect); } static void @@ -675,6 +685,8 @@ ltk_ini_handler(void *window, const char *widget, const char *prop, const char * ltk_button_ini_handler(window, prop, value); } else if (strcmp(widget, "label") == 0) { ltk_label_ini_handler(window, prop, value); + } else if (strcmp(widget, "scrollbar") == 0) { + ltk_scrollbar_ini_handler(window, prop, value); } else { return 0; } @@ -698,6 +710,7 @@ ltk_load_theme(ltk_window *window, const char *path) { ltk_window_setup_theme_defaults(window); ltk_button_setup_theme_defaults(window); ltk_label_setup_theme_defaults(window); + ltk_scrollbar_setup_theme_defaults(window); if (ini_parse(path, ltk_ini_handler, window) < 0) { ltk_warn("Can't load theme.\n"); } @@ -792,7 +805,7 @@ ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) { if (!widget || widget->state == LTK_DISABLED) return; if (event.xbutton.button == 1) { - /* ltk_widget *parent = widget->parent; FIXME */ + /* ltk_widget *parent = widget->parent; FIXME: set pressed widget hierarchy */ widget->state = LTK_PRESSED; if (widget->change_state) widget->change_state(widget); @@ -825,7 +838,8 @@ ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) { void ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) { if (!widget) return; - short pressed = (event.xmotion.state & Button1Mask) == Button1Mask; + /* FIXME: THIS WHOLE STATE HANDLING IS BROKEN */ + int pressed = (event.xmotion.state & Button1Mask) == Button1Mask; if ((widget->state == LTK_NORMAL) && !pressed) { widget->state = LTK_ACTIVE; if (widget->change_state) diff --git a/scrollbar.c b/scrollbar.c @@ -35,10 +35,9 @@ #include "util.h" #include "scrollbar.h" -static void ltk_scrollbar_draw(ltk_scrollbar *scrollbar); +static void ltk_scrollbar_draw(ltk_scrollbar *scrollbar, ltk_rect clip); static void ltk_scrollbar_mouse_press(ltk_scrollbar *scrollbar, XEvent event); static void ltk_scrollbar_motion_notify(ltk_scrollbar *scrollbar, XEvent event); -static ltk_scrollbar *ltk_scrollbar_create(ltk_window *window, ltk_orientation orient); static void ltk_scrollbar_destroy(ltk_scrollbar *scrollbar, int shallow); static struct { @@ -95,8 +94,17 @@ ltk_scrollbar_ini_handler(ltk_window *window, const char *prop, const char *valu } } +void +ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size) { + /* FIXME: some sort of error? */ + if (virtual_size <= 0) + return; + scrollbar->cur_pos = (int)(((double)scrollbar->cur_pos / scrollbar->virtual_size) * virtual_size); + scrollbar->virtual_size = virtual_size; +} + static void -ltk_scrollbar_draw(ltk_scrollbar *scrollbar) { +ltk_scrollbar_draw(ltk_scrollbar *scrollbar, ltk_rect clip) { LtkColor *bg, *fg; int handle_x, handle_y, handle_w, handle_h; ltk_window *window = scrollbar->widget.window; @@ -131,12 +139,18 @@ ltk_scrollbar_draw(ltk_scrollbar *scrollbar) { handle_y = rect.y; handle_h = rect.h; handle_x = (int)(rect.x + (scrollbar->cur_pos / (double)scrollbar->virtual_size) * rect.w); - handle_w = (int)((rect.w / (double)scrollbar->virtual_size) * rect.w); + if (scrollbar->virtual_size > rect.w) + handle_w = (int)((rect.w / (double)scrollbar->virtual_size) * rect.w); + else + handle_w = rect.w; } else { handle_x = rect.x; handle_w = rect.w; - handle_y = (int)(rect.x + (scrollbar->cur_pos / (double)scrollbar->virtual_size) * rect.h); - handle_h = (int)((rect.h / (double)scrollbar->virtual_size) * rect.h); + handle_y = (int)(rect.y + (scrollbar->cur_pos / (double)scrollbar->virtual_size) * rect.h); + if (scrollbar->virtual_size > rect.h) + handle_h = (int)((rect.h / (double)scrollbar->virtual_size) * rect.h); + else + handle_h = rect.h; } if (handle_w <= 0 || handle_h <= 0) return; @@ -151,38 +165,43 @@ ltk_scrollbar_mouse_press(ltk_scrollbar *scrollbar, XEvent event) { } static void -ltk_scrollbar_motion_notify(ltk_scrollbar *scrollbar, XEvent event) { +ltk_scrollbar_motion_notify(ltk_scrollbar *sc, XEvent event) { double scale; int delta, max_pos; - if (scrollbar->widget.state != LTK_PRESSED) + /* FIXME: Make this work properly with LTK_PRESSED */ + if ((event.xmotion.state & Button1Mask) != Button1Mask) return; - if (scrollbar->orient == LTK_HORIZONTAL) { - delta = event.xbutton.x - scrollbar->last_mouse_x; - max_pos = scrollbar->virtual_size - scrollbar->widget.rect.w; - scale = scrollbar->virtual_size / (double)scrollbar->widget.rect.w; + ltk_warn("adasd\n"); + if (sc->orient == LTK_HORIZONTAL) { + delta = event.xbutton.x - sc->last_mouse_x; + max_pos = sc->virtual_size > sc->widget.rect.w ? sc->virtual_size - sc->widget.rect.w : 0; + scale = sc->virtual_size / (double)sc->widget.rect.w; } else { - delta = event.xbutton.y - scrollbar->last_mouse_y; - max_pos = scrollbar->virtual_size - scrollbar->widget.rect.h; - scale = scrollbar->virtual_size / (double)scrollbar->widget.rect.h; + delta = event.xbutton.y - sc->last_mouse_y; + max_pos = sc->virtual_size > sc->widget.rect.h ? sc->virtual_size - sc->widget.rect.h : 0; + scale = sc->virtual_size / (double)sc->widget.rect.h; } - scrollbar->cur_pos += (int)(scale * delta); - if (scrollbar->cur_pos < 0) - scrollbar->cur_pos = 0; - else if (scrollbar->cur_pos > max_pos) - scrollbar->cur_pos = max_pos; - scrollbar->last_mouse_x = event.xbutton.x; - scrollbar->last_mouse_y = event.xbutton.y; + sc->cur_pos += (int)(scale * delta); + if (sc->cur_pos < 0) + sc->cur_pos = 0; + else if (sc->cur_pos > max_pos) + sc->cur_pos = max_pos; + sc->last_mouse_x = event.xbutton.x; + sc->last_mouse_y = event.xbutton.y; } -static ltk_scrollbar * +ltk_scrollbar * ltk_scrollbar_create(ltk_window *window, ltk_orientation orient) { ltk_scrollbar *sc = malloc(sizeof(ltk_scrollbar)); if (!sc) ltk_fatal_errno("Unable to allocate memory for scrollbar.\n"); - ltk_fill_widget_default(sc, NULL, window, &ltk_scrollbar_draw, - NULL, &ltk_scrollbar_destroy, 1); + ltk_fill_widget_defaults(sc, NULL, window, &ltk_scrollbar_draw, + NULL, &ltk_scrollbar_destroy, 1, LTK_UNKNOWN); sc->last_mouse_x = sc->last_mouse_y = 0; - sc->virtual_size = 0; + sc->widget.motion_notify = &ltk_scrollbar_motion_notify; + sc->widget.mouse_press = &ltk_scrollbar_mouse_press; + /* This cannot be 0 because that leads to divide-by-zero */ + sc->virtual_size = 1; sc->cur_pos = 0; sc->orient = orient; if (orient == LTK_HORIZONTAL) diff --git a/scrollbar.h b/scrollbar.h @@ -38,5 +38,6 @@ typedef struct { void ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size); void ltk_scrollbar_setup_theme_defaults(ltk_window *window); void ltk_scrollbar_ini_handler(ltk_window *window, const char *prop, const char *value); +ltk_scrollbar *ltk_scrollbar_create(ltk_window *window, ltk_orientation orient); #endif /* _LTK_SCROLLBAR_H_ */ diff --git a/text.h b/text.h @@ -10,7 +10,7 @@ void ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap void ltk_cleanup_text(void); LtkTextLine *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width); void ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg); -void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y); +void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y, ltk_rect clip); void ltk_text_line_set_width(LtkTextLine *tl, int width); void ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h); void ltk_text_line_destroy(LtkTextLine *tl); diff --git a/text_line.c b/text_line.c @@ -35,14 +35,14 @@ void ltk_cleanup_text(void); LtkTextLine *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width); void ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg); -void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y); +void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y, ltk_rect clip); void ltk_text_line_set_width(LtkTextLine *tl, int width); void ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h); void ltk_text_line_destroy(LtkTextLine *tl); static void ltk_text_line_create_glyphs(struct ltk_text_line *tl); static void ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff, - XImage *img, XColor fg); + XImage *img, XColor fg, ltk_rect clip); static XImage *ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg); @@ -68,7 +68,7 @@ ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg) { /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */ static void -ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff, XImage *img, XColor fg) { +ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff, XImage *img, XColor fg, ltk_rect clip) { int x = glyph->x + xoff; int y = glyph->y + yoff; double a; @@ -95,7 +95,8 @@ ltk_text_line_render( GC gc, Colormap colormap, XColor fg, - XColor bg) + XColor bg, + ltk_rect clip) { ltk_glyph *glyph; @@ -105,7 +106,7 @@ ltk_text_line_render( /* FIXME: pass old image; if it has same dimensions, just clear it */ XImage *img = ltk_create_ximage(dpy, tl->w, tl->h, depth, bg); for (int i = 0; i < tl->glyph_len; i++) { - ltk_text_line_draw_glyph(&tl->glyphs[i], -tl->x_min, -tl->y_min, img, fg); + ltk_text_line_draw_glyph(&tl->glyphs[i], -tl->x_min, -tl->y_min, img, fg, clip); } return img; } diff --git a/text_pango.c b/text_pango.c @@ -98,7 +98,7 @@ ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg) { } void -ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y) { +ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y, ltk_rect clip) { XCopyArea(tm.dpy, tl->pixmap, tl->window, gc, 0, 0, tl->w, tl->h, x, y); } diff --git a/text_stb.c b/text_stb.c @@ -534,7 +534,7 @@ ltk_text_line_render( /* FIXME: error checking if img is rendered yet, tm initialized, etc. */ void -ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y) { +ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y, ltk_rect clip) { XPutImage(tm.dpy, tl->window, gc, tl->img, 0, 0, x, y, tl->w, tl->h); }