feat: MTK scrollbar widget, Wikipedia app improvements
This commit is contained in:
@@ -774,6 +774,122 @@ inline void draw_separator(Canvas& c, int x, int y, int w, const Theme& theme) {
|
||||
c.hline(x, y, w, theme.border);
|
||||
}
|
||||
|
||||
static constexpr int SCROLLBAR_W = 12;
|
||||
static constexpr int SCROLLBAR_OUTER_PAD = 4;
|
||||
static constexpr int SCROLLBAR_TRACK_PAD = 8;
|
||||
static constexpr int SCROLLBAR_THUMB_INSET = 2;
|
||||
static constexpr int SCROLLBAR_MIN_THUMB = 24;
|
||||
|
||||
inline bool scrollbar_needed(int content_extent, int view_extent) {
|
||||
return content_extent > view_extent && view_extent > 0;
|
||||
}
|
||||
|
||||
inline int scrollbar_max_offset(int content_extent, int view_extent) {
|
||||
int max_offset = content_extent - view_extent;
|
||||
return max_offset > 0 ? max_offset : 0;
|
||||
}
|
||||
|
||||
inline Rect scrollbar_track_rect(const Rect& viewport,
|
||||
int width = SCROLLBAR_W,
|
||||
int outer_pad = SCROLLBAR_OUTER_PAD,
|
||||
int track_pad = SCROLLBAR_TRACK_PAD) {
|
||||
int h = gui_max(viewport.h - track_pad * 2, 0);
|
||||
int usable_w = viewport.w - outer_pad;
|
||||
if (usable_w <= 0 || h <= 0) return {0, 0, 0, 0};
|
||||
int actual_w = gui_clamp(width, 1, usable_w);
|
||||
return {
|
||||
viewport.x + viewport.w - actual_w - outer_pad,
|
||||
viewport.y + track_pad,
|
||||
actual_w,
|
||||
h
|
||||
};
|
||||
}
|
||||
|
||||
inline int scrollbar_thumb_extent(int content_extent,
|
||||
int view_extent,
|
||||
int track_extent,
|
||||
int min_thumb = SCROLLBAR_MIN_THUMB) {
|
||||
if (!scrollbar_needed(content_extent, view_extent) || track_extent <= 0)
|
||||
return track_extent;
|
||||
|
||||
int thumb = (int)(((int64_t)view_extent * track_extent) / content_extent);
|
||||
return gui_clamp(thumb, gui_min(min_thumb, track_extent), track_extent);
|
||||
}
|
||||
|
||||
inline Rect scrollbar_thumb_rect(const Rect& track,
|
||||
int content_extent,
|
||||
int view_extent,
|
||||
int offset,
|
||||
int min_thumb = SCROLLBAR_MIN_THUMB,
|
||||
int thumb_inset = SCROLLBAR_THUMB_INSET) {
|
||||
if (track.empty() || !scrollbar_needed(content_extent, view_extent))
|
||||
return {0, 0, 0, 0};
|
||||
|
||||
int max_offset = scrollbar_max_offset(content_extent, view_extent);
|
||||
int thumb_h = scrollbar_thumb_extent(content_extent, view_extent,
|
||||
track.h, min_thumb);
|
||||
int travel = gui_max(track.h - thumb_h, 0);
|
||||
int clamped_offset = gui_clamp(offset, 0, max_offset);
|
||||
int thumb_y = track.y + (max_offset > 0
|
||||
? (int)(((int64_t)clamped_offset * travel) / max_offset)
|
||||
: 0);
|
||||
int thumb_w = gui_max(track.w - thumb_inset * 2, 1);
|
||||
return {track.x + thumb_inset, thumb_y, thumb_w, thumb_h};
|
||||
}
|
||||
|
||||
inline int scrollbar_offset_from_thumb_top(const Rect& track,
|
||||
int content_extent,
|
||||
int view_extent,
|
||||
int thumb_extent,
|
||||
int thumb_top) {
|
||||
int max_offset = scrollbar_max_offset(content_extent, view_extent);
|
||||
int travel = gui_max(track.h - thumb_extent, 0);
|
||||
if (track.empty() || max_offset <= 0 || travel <= 0)
|
||||
return 0;
|
||||
|
||||
int rel = gui_clamp(thumb_top - track.y, 0, travel);
|
||||
return (int)(((int64_t)rel * max_offset + travel / 2) / travel);
|
||||
}
|
||||
|
||||
inline Color scrollbar_thumb_color(const Theme& theme,
|
||||
bool hovered,
|
||||
bool dragging) {
|
||||
Color base = mix(theme.border, theme.text_muted, 54);
|
||||
if (dragging) return mix(base, theme.accent, 118);
|
||||
if (hovered) return mix(base, theme.accent, 42);
|
||||
return base;
|
||||
}
|
||||
|
||||
inline void draw_scrollbar(Canvas& c,
|
||||
const Rect& track,
|
||||
const Rect& thumb,
|
||||
bool hovered,
|
||||
bool dragging,
|
||||
const Theme& theme) {
|
||||
if (track.empty() || thumb.empty()) return;
|
||||
|
||||
if (hovered || dragging) {
|
||||
int rail_w = track.w > 6 ? track.w - 6 : track.w;
|
||||
if (rail_w < 1) rail_w = 1;
|
||||
Rect rail = {
|
||||
track.x + (track.w - rail_w) / 2,
|
||||
track.y,
|
||||
rail_w,
|
||||
track.h
|
||||
};
|
||||
Color rail_fill = dragging
|
||||
? mix(theme.window_bg, theme.accent, 34)
|
||||
: mix(theme.window_bg, theme.text_muted, 24);
|
||||
c.fill_rounded_rect(rail.x, rail.y, rail.w, rail.h,
|
||||
gui_max(theme.radius_sm, rail.w / 2),
|
||||
rail_fill);
|
||||
}
|
||||
|
||||
Color thumb_fill = scrollbar_thumb_color(theme, hovered, dragging);
|
||||
int radius = gui_max(theme.radius_sm, thumb.w / 2);
|
||||
c.fill_rounded_rect(thumb.x, thumb.y, thumb.w, thumb.h, radius, thumb_fill);
|
||||
}
|
||||
|
||||
inline Rect tab_rect(const Rect& bar, int count, int index) {
|
||||
if (count <= 0) return {bar.x, bar.y, 0, bar.h};
|
||||
int slot_w = bar.w / count;
|
||||
|
||||
Reference in New Issue
Block a user