mii_emu/libmui/mui/mui_cdef_scrollbar.c

367 lines
9.5 KiB
C

/*
* mui_cdef_scrollbar.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "mui.h"
#include "cg.h"
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
enum mui_sb_part_e {
MUI_SB_PART_FRAME = 0,
MUI_SB_PART_UP,
MUI_SB_PART_DOWN,
MUI_SB_PART_PAGEUP,
MUI_SB_PART_PAGEDOWN,
MUI_SB_PART_THUMB,
MUI_SB_PART_COUNT,
};
typedef struct mui_scrollbar_control_t {
mui_control_t control;
uint32_t visible;
uint32_t max;
c2_pt_t drag_offset;
uint32_t saved_value; // to handle 'snapback'
} mui_scrollbar_control_t;
static void
mui_scrollbar_make_rects(
mui_control_t * c,
c2_rect_t * parts)
{
c2_rect_t f = c->frame;
c2_rect_offset(&f, c->win->content.l, c->win->content.t);
parts[MUI_SB_PART_FRAME] = f;
c2_rect_t part = f;
part.b = part.t + c2_rect_width(&part);
parts[MUI_SB_PART_UP] = part;
part = f;
part.t = part.b - c2_rect_width(&part);
parts[MUI_SB_PART_DOWN] = part;
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
if (sb->max <= sb->visible) {
c2_rect_t z = {};
parts[MUI_SB_PART_THUMB] = z;
parts[MUI_SB_PART_PAGEUP] = z;
parts[MUI_SB_PART_PAGEDOWN] = z;
return;
}
part = f;
part.t = parts[MUI_SB_PART_UP].b + 1;
part.b = parts[MUI_SB_PART_DOWN].t - 1;
float visible = sb->visible / (float)sb->max;
float thumb_size = visible * sb->visible;
if (thumb_size < 20)
thumb_size = 20;
float thumb_pos = c->value / ((float)sb->max- sb->visible);
float thumb_offset = 0.5 + thumb_pos * (c2_rect_height(&part) - thumb_size);
// printf("%s visible:%.2f ts: %.2f thumb_pos:%.2f thumb_offset:%.2f\n",
// __func__, visible, thumb_size, thumb_pos, thumb_offset);
part.b = part.t + thumb_size;
c2_rect_offset(&part, 0, thumb_offset);
if (part.b > parts[MUI_SB_PART_DOWN].t) {
c2_rect_offset(&part, 0, parts[MUI_SB_PART_DOWN].t - part.b);
}
parts[MUI_SB_PART_THUMB] = part;
part = f;
part.t = parts[MUI_SB_PART_UP].b + 1;
part.b = parts[MUI_SB_PART_THUMB].t - 1;
parts[MUI_SB_PART_PAGEUP] = part;
part = f;
part.t = parts[MUI_SB_PART_THUMB].b + 1;
part.b = parts[MUI_SB_PART_DOWN].t - 1;
parts[MUI_SB_PART_PAGEDOWN] = part;
}
static void
mui_scrollbar_draw(
mui_window_t * win,
mui_control_t * c,
mui_drawable_t *dr )
{
c2_rect_t f = c->frame;
c2_rect_offset(&f, win->content.l, win->content.t);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
cg_set_line_width(cg, 1);
cg_rectangle(cg, f.l, f.t,
c2_rect_width(&f), c2_rect_height(&f));
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
mui_font_t * icons = mui_font_find(win->ui, "icon_small");
c2_rect_t parts[MUI_SB_PART_COUNT];
mui_scrollbar_make_rects(c, parts);
mui_color_t contentFill = MUI_COLOR(0xa0a0a0ff);
mui_color_t decoColor = MUI_COLOR(0x666666ff);
c2_rect_t pf;
pf = parts[MUI_SB_PART_UP];
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_UP ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
stb_ttc_measure m = {};
mui_font_text_measure(icons, "", &m);
pf.l = pf.l + (c2_rect_width(&pf) - m.x1 - m.x0) / 2;
mui_font_text_draw(icons, dr, pf.tl, "", 0,
mui_control_color[c->state].text);
pf = parts[MUI_SB_PART_DOWN];
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_DOWN ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
mui_font_text_measure(icons, "", &m);
pf.l = pf.l + (c2_rect_width(&pf) - m.x1 - m.x0) / 2;
mui_font_text_draw(icons, dr, pf.tl, "", 0,
mui_control_color[c->state].text);
pf = parts[MUI_SB_PART_PAGEUP];
if (c2_rect_height(&pf) > 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_PAGEUP ?
&CG_COLOR(decoColor) :
&CG_COLOR(contentFill));
cg_fill(cg);
}
pf = parts[MUI_SB_PART_PAGEDOWN];
if (c2_rect_height(&pf) > 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_PAGEDOWN ?
&CG_COLOR(decoColor) :
&CG_COLOR(contentFill));
cg_fill(cg);
}
pf = parts[MUI_SB_PART_THUMB];
if (c2_rect_height(&pf) > 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_THUMB ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg,
&CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
}
}
static void
_mui_scrollbar_scroll(
mui_control_t * c,
int32_t delta )
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
int32_t v = (int32_t)c->value + delta;
if (v < 0)
v = 0;
if (v > ((int32_t)sb->max - (int32_t)sb->visible))
v = sb->max - sb->visible;
c->value = v;
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
}
static bool
mui_scrollbar_mouse(
struct mui_control_t * c,
mui_event_t * ev)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
c2_rect_t parts[MUI_SB_PART_COUNT];
mui_scrollbar_make_rects(c, parts);
switch (ev->type) {
case MUI_EVENT_BUTTONDOWN: {
for (int i = 1; i < MUI_SB_PART_COUNT; ++i) {
if (c2_rect_contains_pt(&parts[i], &ev->mouse.where)) {
c->flags.hit_part = i;
sb->drag_offset.x =
ev->mouse.where.x - parts[i].l;
sb->drag_offset.y =
ev->mouse.where.y - parts[i].t;
sb->saved_value = c->value;
break;
}
}
int part = c->flags.hit_part % MUI_SB_PART_COUNT;
switch (part) {
case MUI_SB_PART_DOWN:
case MUI_SB_PART_UP: {
_mui_scrollbar_scroll(c,
part == MUI_SB_PART_UP ? -30 : 30);
} break;
case MUI_SB_PART_PAGEUP:
case MUI_SB_PART_PAGEDOWN: {
int32_t offset = sb->visible;
_mui_scrollbar_scroll(c,
part == MUI_SB_PART_PAGEUP ?
-offset : offset);
} break;
case MUI_SB_PART_THUMB:
mui_control_inval(c);
break;
}
// printf("%s hit part %d\n", __func__, c->flags.hit_part);
} break;
case MUI_EVENT_DRAG: {
if (!c->flags.hit_part)
break;
int part = c->flags.hit_part % MUI_SB_PART_COUNT;
c2_rect_t test_rect = parts[part];
if (part == MUI_SB_PART_THUMB)
c2_rect_inset(&test_rect, -60, -60);
if (c2_rect_contains_pt(&test_rect, &ev->mouse.where)) {
c->flags.hit_part = part;
switch (part) {
case MUI_SB_PART_THUMB: {
c2_rect_t nt = parts[part];
c2_rect_offset(&nt, 0,
-(nt.t + parts[MUI_SB_PART_UP].b) +
ev->mouse.where.y - sb->drag_offset.y);
// printf("%s thumb %s\n", __func__, c2_rect_as_str(&nt));
if (nt.t < 0)
c2_rect_offset(&nt, 0, -nt.t);
if (nt.b > parts[MUI_SB_PART_DOWN].t)
c2_rect_offset(&nt, 0, parts[MUI_SB_PART_DOWN].t - nt.b);
int max_pixels = parts[MUI_SB_PART_DOWN].t -
parts[MUI_SB_PART_UP].b -
c2_rect_height(&nt);
uint32_t nv = nt.t * (sb->max - sb->visible) / max_pixels;
if (nv > (sb->max - sb->visible))
nv = sb->max - sb->visible;
c->value = nv;
// printf("v is %d vs %d max %d = %d new val %d\n",
// nt.t, max_pixels, sb->max, nv,
// mui_control_get_value(c));
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
} break;
}
} else {
c->flags.hit_part = part + MUI_SB_PART_COUNT;
if (part == MUI_SB_PART_THUMB) {
c->value = sb->saved_value;
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
}
}
} break;
case MUI_EVENT_BUTTONUP: {
if (!c->flags.hit_part)
break;
mui_control_inval(c);
c->flags.hit_part = 0;
} break;
}
return true;
}
static bool
mui_cdef_scrollbar(
struct mui_control_t * c,
uint8_t what,
void * param)
{
switch (what) {
case MUI_CDEF_INIT:
break;
case MUI_CDEF_DISPOSE:
break;
case MUI_CDEF_DRAW: {
mui_drawable_t * dr = param;
mui_scrollbar_draw(c->win, c, dr);
} break;
case MUI_CDEF_EVENT: {
// printf("%s event\n", __func__);
mui_event_t *ev = param;
switch (ev->type) {
case MUI_EVENT_BUTTONUP:
case MUI_EVENT_DRAG:
case MUI_EVENT_BUTTONDOWN: {
return mui_scrollbar_mouse(c, ev);
} break;
}
} break;
}
return false;
}
uint32_t
mui_scrollbar_get_max(
mui_control_t * c)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
return sb->max;
}
void
mui_scrollbar_set_max(
mui_control_t * c,
uint32_t max)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
sb->max = max;
mui_control_inval(c);
}
void
mui_scrollbar_set_page(
mui_control_t * c,
uint32_t page)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
sb->visible = page;
mui_control_inval(c);
}
mui_control_t *
mui_scrollbar_new(
mui_window_t * win,
c2_rect_t frame,
uint32_t uid )
{
return mui_control_new(
win, MUI_CONTROL_SCROLLBAR, mui_cdef_scrollbar,
frame, NULL, uid,
sizeof(mui_scrollbar_control_t));
}