Compare commits
4 Commits
fbb577f3f8
...
e90abbd03c
| Author | SHA1 | Date | |
|---|---|---|---|
|
e90abbd03c
|
|||
|
2453d4d0b1
|
|||
|
0af42f975c
|
|||
|
d4f81ffa89
|
+517
-27
@@ -16,6 +16,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// This file includes some common stuff to build terminal/X11 applications with.
|
||||
// It assumes you've already included liberty.c, and may include liberty-xdg.c.
|
||||
|
||||
@@ -599,7 +601,7 @@ struct widget
|
||||
|
||||
int widget_id; ///< Post-layouting identification
|
||||
int userdata; ///< Action ID/Tab index/...
|
||||
char text[]; ///< Any text label
|
||||
char *text; ///< Any text label
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -621,13 +623,99 @@ widget_move (struct widget *w, int dx, int dy)
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
// Use {INFINITY, INFINITY} once for stopping a subpath, twice to end icon data.
|
||||
struct xui_icon_point
|
||||
{
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
struct xui_symbol
|
||||
{
|
||||
struct widget widget;
|
||||
const struct xui_icon_point *icon;
|
||||
};
|
||||
|
||||
struct xui_gauge
|
||||
{
|
||||
struct widget widget;
|
||||
double fraction;
|
||||
};
|
||||
|
||||
struct xui_scrollbar
|
||||
{
|
||||
struct widget widget;
|
||||
long top;
|
||||
long total;
|
||||
};
|
||||
|
||||
// XXX: This is a rather dirty simplification.
|
||||
static struct widget *
|
||||
xui_new_symbol (struct widget *label, const struct xui_icon_point *icon)
|
||||
{
|
||||
if (!icon)
|
||||
return label;
|
||||
|
||||
size_t len = strlen (label->text) + 1;
|
||||
struct xui_symbol *self = xcalloc (1, sizeof *self + len);
|
||||
self->widget = *label;
|
||||
self->widget.text = memcpy (self + 1, label->text, len);
|
||||
self->icon = icon;
|
||||
free (label);
|
||||
return &self->widget;
|
||||
}
|
||||
|
||||
struct xui_scrollbar_part
|
||||
{
|
||||
long length;
|
||||
long start;
|
||||
};
|
||||
|
||||
/// Figure out scrollbar appearance. @a s is the minimal slider length as well
|
||||
/// as the scrollbar resolution per visible item.
|
||||
static struct xui_scrollbar_part
|
||||
xui_compute_scrollbar (const struct xui_scrollbar *self, long visible, long s)
|
||||
{
|
||||
long top = s * self->top, total = s * self->total;
|
||||
if (total < visible)
|
||||
return (struct xui_scrollbar_part) { 0, 0 };
|
||||
if (visible == 1)
|
||||
return (struct xui_scrollbar_part) { s, 0 };
|
||||
if (visible == 2)
|
||||
return (struct xui_scrollbar_part)
|
||||
{ s, top >= total / 2 ? s : 0 };
|
||||
|
||||
// Only be at the top or bottom when the top or bottom item can be seen.
|
||||
// The algorithm isn't optimal but it's a bitch to get right.
|
||||
double available_length = visible - 2 - s + 1;
|
||||
|
||||
double lenf = s + available_length * visible / total, length = 0.;
|
||||
long offset = 1 + available_length * top / total + modf (lenf, &length);
|
||||
|
||||
if (top == 0)
|
||||
return (struct xui_scrollbar_part) { length, 0 };
|
||||
if (top + visible >= total)
|
||||
return (struct xui_scrollbar_part) { length, visible - length };
|
||||
|
||||
return (struct xui_scrollbar_part) { length, offset };
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
struct ui
|
||||
{
|
||||
struct widget *(*padding)
|
||||
(chtype attrs, float width, float height);
|
||||
struct widget *(*label)
|
||||
(chtype attrs, unsigned extended, const char *label);
|
||||
struct widget *(*symbol)
|
||||
(chtype attrs, const char *label, const struct xui_icon_point *icon);
|
||||
struct widget *(*gauge)
|
||||
(chtype attrs, double fraction);
|
||||
struct widget *(*scrollbar)
|
||||
(chtype attrs, long top, long total);
|
||||
|
||||
void (*beep) (void);
|
||||
void (*render) (void);
|
||||
void (*flip) (void);
|
||||
void (*winch) (void);
|
||||
@@ -683,6 +771,7 @@ struct xui
|
||||
struct poller_fd tty_event; ///< Terminal input event
|
||||
struct poller_timer tk_timer; ///< termo timeout timer
|
||||
bool locale_is_utf8; ///< The locale is Unicode
|
||||
bool use_partial_boxes; ///< Use Unicode box drawing chars
|
||||
|
||||
// X11:
|
||||
|
||||
@@ -770,8 +859,8 @@ tui_render_padding (struct widget *self)
|
||||
static struct widget *
|
||||
tui_make_padding (chtype attrs, float width, float height)
|
||||
{
|
||||
struct widget *w = xcalloc (1, sizeof *w + 2);
|
||||
w->text[0] = ' ';
|
||||
struct widget *w = xcalloc (1, sizeof *w);
|
||||
w->text = " ";
|
||||
w->on_render = tui_render_padding;
|
||||
w->attrs = attrs;
|
||||
w->width = width * 2;
|
||||
@@ -790,10 +879,9 @@ tui_render_label (struct widget *self)
|
||||
static struct widget *
|
||||
tui_make_label (chtype attrs, unsigned extended, const char *label)
|
||||
{
|
||||
(void) extended;
|
||||
|
||||
size_t len = strlen (label);
|
||||
struct widget *w = xcalloc (1, sizeof *w + len + 1);
|
||||
w->text = (char *) (w + 1);
|
||||
w->on_render = tui_render_label;
|
||||
w->attrs = attrs;
|
||||
w->extended_attrs = extended;
|
||||
@@ -807,6 +895,127 @@ tui_make_label (chtype attrs, unsigned extended, const char *label)
|
||||
return w;
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
tui_make_symbol (chtype attrs, const char *label,
|
||||
const struct xui_icon_point *icon)
|
||||
{
|
||||
(void) icon;
|
||||
return tui_make_label (attrs, 0, label);
|
||||
}
|
||||
|
||||
static void
|
||||
tui_render_gauge (struct widget *widget)
|
||||
{
|
||||
const struct xui_gauge *self = (const struct xui_gauge *) widget;
|
||||
struct row_buffer buf = row_buffer_make ();
|
||||
|
||||
// Always compute it in exactly eight times the resolution,
|
||||
// because sometimes Unicode is even useful
|
||||
int len_left = self->fraction * widget->width * 8 + 0.5;
|
||||
|
||||
static const char *partials[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉" };
|
||||
int remainder = len_left % 8;
|
||||
len_left /= 8;
|
||||
|
||||
const char *partial = NULL;
|
||||
if (g_xui.use_partial_boxes)
|
||||
partial = partials[remainder];
|
||||
else
|
||||
len_left += remainder >= 4;
|
||||
|
||||
int len_right = widget->width - len_left;
|
||||
row_buffer_space (&buf, len_left, widget->attrs ^ A_REVERSE);
|
||||
if (partial && len_right-- > 0)
|
||||
row_buffer_append (&buf, partial, widget->attrs);
|
||||
row_buffer_space (&buf, len_right, widget->attrs);
|
||||
tui_flush_buffer (widget, &buf);
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
tui_make_gauge (chtype attrs, double fraction)
|
||||
{
|
||||
struct xui_gauge *self = xcalloc (1, sizeof *self);
|
||||
self->widget.text = "";
|
||||
self->widget.on_render = tui_render_gauge;
|
||||
self->widget.attrs = attrs;
|
||||
self->widget.width = -1;
|
||||
self->widget.height = 1;
|
||||
self->fraction = MIN (MAX (fraction, 0.), 1.);
|
||||
return &self->widget;
|
||||
}
|
||||
|
||||
static void
|
||||
tui_render_scrollbar (struct widget *widget)
|
||||
{
|
||||
// This assumes that we can write to the column, i.e.,
|
||||
// that it's not covered by any double-wide character (and that
|
||||
// ncurses comes to the right results when counting characters).
|
||||
struct xui_scrollbar *self = (struct xui_scrollbar *) widget;
|
||||
int visible = widget->height;
|
||||
|
||||
if (!g_xui.use_partial_boxes)
|
||||
{
|
||||
struct xui_scrollbar_part bar =
|
||||
xui_compute_scrollbar (self, visible, 1);
|
||||
for (int row = 0; row < visible; row++)
|
||||
{
|
||||
move (widget->y + row, widget->x);
|
||||
chtype attrs = widget->attrs;
|
||||
if (row >= bar.start && row < bar.start + bar.length)
|
||||
attrs ^= A_REVERSE;
|
||||
addch (' ' | attrs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct xui_scrollbar_part bar =
|
||||
xui_compute_scrollbar (self, visible * 8, 8);
|
||||
bar.length += bar.start;
|
||||
|
||||
int start_part = bar.start % 8; bar.start /= 8;
|
||||
int end_part = bar.length % 8; bar.length /= 8;
|
||||
|
||||
// Even with this, the solid part must reach full cell boundaries.
|
||||
// It turns out trying to use '█' is plainly asking for problems,
|
||||
// so we turn it into inversion.
|
||||
static const char *partials[] = { " ", "▇", "▆", "▅", "▄", "▃", "▂", "▁" };
|
||||
|
||||
for (int row = 0; row < visible; row++)
|
||||
{
|
||||
chtype attrs = widget->attrs;
|
||||
if (row >= bar.start + !!start_part
|
||||
&& row < bar.length + !!end_part)
|
||||
attrs ^= A_REVERSE;
|
||||
|
||||
const char *glyph = NULL;
|
||||
if (row == bar.start) glyph = partials[start_part];
|
||||
if (row == bar.length) glyph = partials[end_part];
|
||||
|
||||
move (widget->y + row, widget->x);
|
||||
|
||||
struct row_buffer buf = row_buffer_make ();
|
||||
if (glyph)
|
||||
row_buffer_append (&buf, glyph, attrs);
|
||||
else
|
||||
row_buffer_append_c (&buf, ' ', attrs);
|
||||
row_buffer_flush (&buf);
|
||||
row_buffer_free (&buf);
|
||||
}
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
tui_make_scrollbar (chtype attrs, long top, long total)
|
||||
{
|
||||
struct xui_scrollbar *self = xcalloc (1, sizeof *self);
|
||||
self->widget.text = "";
|
||||
self->widget.on_render = tui_render_scrollbar;
|
||||
self->widget.attrs = attrs;
|
||||
self->widget.width = 1;
|
||||
self->top = top;
|
||||
self->total = total;
|
||||
return &self->widget;
|
||||
}
|
||||
|
||||
static void
|
||||
tui_render_widgets (struct widget *head)
|
||||
{
|
||||
@@ -820,6 +1029,12 @@ tui_render_widgets (struct widget *head)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tui_beep (void)
|
||||
{
|
||||
beep ();
|
||||
}
|
||||
|
||||
static void
|
||||
tui_render (void)
|
||||
{
|
||||
@@ -869,7 +1084,11 @@ static struct ui tui_ui =
|
||||
{
|
||||
.padding = tui_make_padding,
|
||||
.label = tui_make_label,
|
||||
.symbol = tui_make_symbol,
|
||||
.gauge = tui_make_gauge,
|
||||
.scrollbar = tui_make_scrollbar,
|
||||
|
||||
.beep = tui_beep,
|
||||
.render = tui_render,
|
||||
.flip = tui_flip,
|
||||
.winch = tui_winch,
|
||||
@@ -905,10 +1124,10 @@ tui_on_tty_event (termo_key_t *event, int64_t event_ts)
|
||||
last_button = button;
|
||||
|
||||
if (!app_process_mouse (type, x, y, button, modifiers))
|
||||
beep ();
|
||||
tui_beep ();
|
||||
}
|
||||
else if (!xui_process_termo_event (event))
|
||||
beep ();
|
||||
tui_beep ();
|
||||
|
||||
last_event = *event;
|
||||
last_event_ts = event_ts;
|
||||
@@ -944,7 +1163,7 @@ tui_on_key_timer (void *user_data)
|
||||
termo_key_t event;
|
||||
if (termo_getkey_force (g_xui.tk, &event) == TERMO_RES_KEY)
|
||||
if (!xui_process_termo_event (&event))
|
||||
beep ();
|
||||
tui_beep ();
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1027,11 +1246,9 @@ x11_font_open (unsigned style)
|
||||
? FcNameParse ((const FcChar8 *) g_xui.x11_fontname_monospace)
|
||||
: FcNameParse ((const FcChar8 *) g_xui.x11_fontname);
|
||||
if (style & X11_FONT_BOLD)
|
||||
FcPatternAdd (pattern, FC_STYLE, (FcValue) {
|
||||
.type = FcTypeString, .u.s = (FcChar8 *) "Bold" }, FcFalse);
|
||||
FcPatternAddInteger (pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
|
||||
if (style & X11_FONT_ITALIC)
|
||||
FcPatternAdd (pattern, FC_STYLE, (FcValue) {
|
||||
.type = FcTypeString, .u.s = (FcChar8 *) "Italic" }, FcFalse);
|
||||
FcPatternAddInteger (pattern, FC_SLANT, FC_SLANT_ITALIC);
|
||||
|
||||
FcPattern *substituted = FcPatternDuplicate (pattern);
|
||||
FcConfigSubstitute (NULL, substituted, FcMatchPattern);
|
||||
@@ -1259,8 +1476,8 @@ x11_render_padding (struct widget *self)
|
||||
static struct widget *
|
||||
x11_make_padding (chtype attrs, float width, float height)
|
||||
{
|
||||
struct widget *w = xcalloc (1, sizeof *w + 2);
|
||||
w->text[0] = ' ';
|
||||
struct widget *w = xcalloc (1, sizeof *w);
|
||||
w->text = " ";
|
||||
w->on_render = x11_render_padding;
|
||||
w->attrs = attrs;
|
||||
w->width = g_xui.vunit * width;
|
||||
@@ -1325,6 +1542,7 @@ x11_make_label (chtype attrs, unsigned extended, const char *label)
|
||||
}
|
||||
|
||||
struct widget *w = xcalloc (1, sizeof *w + normalized_len);
|
||||
w->text = (char *) (w + 1);
|
||||
w->on_render = x11_render_label;
|
||||
w->attrs = attrs;
|
||||
w->extended_attrs = extended;
|
||||
@@ -1337,6 +1555,131 @@ x11_make_label (chtype attrs, unsigned extended, const char *label)
|
||||
return w;
|
||||
}
|
||||
|
||||
static void
|
||||
x11_render_symbol (struct widget *widget)
|
||||
{
|
||||
x11_render_padding (widget);
|
||||
|
||||
const struct xui_icon_point *icon =
|
||||
((const struct xui_symbol *) widget)->icon;
|
||||
size_t total = 0;
|
||||
while (!isinf (icon[total].x) || !isinf (icon[total].y)
|
||||
|| !isinf (icon[total + 1].x) || !isinf (icon[total + 1].y))
|
||||
total++;
|
||||
|
||||
// TODO: There might be an attribute for symbols, to handle this better.
|
||||
XRenderColor color = *x11_fg (widget);
|
||||
if (!(widget->attrs & A_BOLD))
|
||||
{
|
||||
color.alpha /= 2;
|
||||
color.red /= 2;
|
||||
color.green /= 2;
|
||||
color.blue /= 2;
|
||||
}
|
||||
|
||||
Picture source = XRenderCreateSolidFill (g_xui.dpy, &color);
|
||||
const XRenderPictFormat *format
|
||||
= XRenderFindStandardFormat (g_xui.dpy, PictStandardA8);
|
||||
|
||||
int x = widget->x, y = widget->y + (widget->height - widget->width) / 2;
|
||||
XPointDouble buffer[total], *p = buffer;
|
||||
for (size_t i = 0; i <= total; i++)
|
||||
if (icon[i].x != INFINITY)
|
||||
{
|
||||
p->x = x + icon[i].x / 20.0 * widget->width;
|
||||
p->y = y + icon[i].y / 20.0 * widget->width;
|
||||
p++;
|
||||
}
|
||||
else if (p != buffer)
|
||||
{
|
||||
XRenderCompositeDoublePoly (g_xui.dpy, PictOpOver,
|
||||
source, g_xui.x11_pixmap_picture, format,
|
||||
0, 0, 0, 0, buffer, p - buffer, EvenOddRule);
|
||||
p = buffer;
|
||||
}
|
||||
XRenderFreePicture (g_xui.dpy, source);
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
x11_make_symbol (chtype attrs, const char *label,
|
||||
const struct xui_icon_point *icon)
|
||||
{
|
||||
struct widget *w =
|
||||
xui_new_symbol (x11_make_label (attrs, 0, label), icon);
|
||||
if (icon)
|
||||
{
|
||||
w->on_render = x11_render_symbol;
|
||||
|
||||
// It should be padded by the caller horizontally.
|
||||
w->height = g_xui.vunit;
|
||||
w->width = w->height * 3 / 4;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
static void
|
||||
x11_render_gauge (struct widget *widget)
|
||||
{
|
||||
const struct xui_gauge *self = (const struct xui_gauge *) widget;
|
||||
|
||||
int part = self->fraction * widget->width;
|
||||
XRenderFillRectangle (g_xui.dpy, PictOpSrc, g_xui.x11_pixmap_picture,
|
||||
x11_fg_attrs (widget->attrs),
|
||||
widget->x,
|
||||
widget->y,
|
||||
part,
|
||||
widget->height);
|
||||
XRenderFillRectangle (g_xui.dpy, PictOpSrc, g_xui.x11_pixmap_picture,
|
||||
x11_bg_attrs (widget->attrs),
|
||||
widget->x + part,
|
||||
widget->y,
|
||||
widget->width - part,
|
||||
widget->height);
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
x11_make_gauge (chtype attrs, double fraction)
|
||||
{
|
||||
struct xui_gauge *self = xcalloc (1, sizeof *self);
|
||||
self->widget.text = "";
|
||||
self->widget.on_render = x11_render_gauge;
|
||||
self->widget.attrs = attrs;
|
||||
self->widget.width = -1;
|
||||
self->widget.height = g_xui.vunit;
|
||||
self->fraction = MIN (MAX (fraction, 0.), 1.);
|
||||
return &self->widget;
|
||||
}
|
||||
|
||||
static void
|
||||
x11_render_scrollbar (struct widget *widget)
|
||||
{
|
||||
struct xui_scrollbar *self = (struct xui_scrollbar *) widget;
|
||||
x11_render_padding (widget);
|
||||
|
||||
struct xui_scrollbar_part bar =
|
||||
xui_compute_scrollbar (self, widget->height, g_xui.vunit);
|
||||
|
||||
XRenderFillRectangle (g_xui.dpy, PictOpSrc, g_xui.x11_pixmap_picture,
|
||||
x11_fg_attrs (widget->attrs),
|
||||
widget->x,
|
||||
widget->y + bar.start,
|
||||
widget->width,
|
||||
bar.length);
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
x11_make_scrollbar (chtype attrs, long top, long total)
|
||||
{
|
||||
struct xui_scrollbar *self = xcalloc (1, sizeof *self);
|
||||
self->widget.text = "";
|
||||
self->widget.on_render = x11_render_scrollbar;
|
||||
self->widget.attrs = attrs;
|
||||
self->widget.width = g_xui.vunit / 2;
|
||||
self->top = top;
|
||||
self->total = total;
|
||||
return &self->widget;
|
||||
}
|
||||
|
||||
static void
|
||||
x11_render_widget (struct widget *w, const XRectangle *clip)
|
||||
{
|
||||
@@ -1346,9 +1689,17 @@ x11_render_widget (struct widget *w, const XRectangle *clip)
|
||||
// Children may set their own clips, so reset before each sibling.
|
||||
// We need to go through Xft, or XftTextRenderUtf8() might skip glyphs.
|
||||
if (clip)
|
||||
{
|
||||
XftDrawSetClipRectangles (g_xui.xft_draw, 0, 0, clip, 1);
|
||||
XRenderSetPictureClipRectangles (g_xui.dpy,
|
||||
g_xui.x11_pixmap_picture, 0, 0, clip, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
XftDrawSetClip (g_xui.xft_draw, None);
|
||||
XRenderChangePicture (g_xui.dpy, g_xui.x11_pixmap_picture, CPClipMask,
|
||||
&(XRenderPictureAttributes) { .clip_mask = None });
|
||||
}
|
||||
|
||||
if (w->on_render)
|
||||
w->on_render (w);
|
||||
@@ -1374,6 +1725,13 @@ x11_render_widget (struct widget *w, const XRectangle *clip)
|
||||
x11_render_widget (child, &subclip);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_beep (void)
|
||||
{
|
||||
XkbBell (g_xui.dpy, g_xui.x11_window, 0, None);
|
||||
XFlush (g_xui.dpy);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_render (void)
|
||||
{
|
||||
@@ -1431,7 +1789,11 @@ static struct ui x11_ui =
|
||||
{
|
||||
.padding = x11_make_padding,
|
||||
.label = x11_make_label,
|
||||
.symbol = x11_make_symbol,
|
||||
.gauge = x11_make_gauge,
|
||||
.scrollbar = x11_make_scrollbar,
|
||||
|
||||
.beep = x11_beep,
|
||||
.render = x11_render,
|
||||
.flip = x11_flip,
|
||||
.destroy = x11_destroy,
|
||||
@@ -2154,8 +2516,8 @@ appkit_render_padding (struct widget *self)
|
||||
static struct widget *
|
||||
appkit_make_padding (chtype attrs, float width, float height)
|
||||
{
|
||||
struct widget *w = xcalloc (1, sizeof *w + 2);
|
||||
w->text[0] = ' ';
|
||||
struct widget *w = xcalloc (1, sizeof *w);
|
||||
w->text = " ";
|
||||
w->on_render = appkit_render_padding;
|
||||
w->attrs = attrs;
|
||||
w->width = g_xui.vunit * width;
|
||||
@@ -2183,6 +2545,7 @@ appkit_make_label (chtype attrs, unsigned extended, const char *label)
|
||||
size_t label_len = strlen (label) + 1;
|
||||
|
||||
struct widget *w = xcalloc (1, sizeof *w + label_len);
|
||||
w->text = (char *) (w + 1);
|
||||
w->on_render = appkit_render_label;
|
||||
w->attrs = attrs;
|
||||
w->extended_attrs = extended;
|
||||
@@ -2194,6 +2557,118 @@ appkit_make_label (chtype attrs, unsigned extended, const char *label)
|
||||
return w;
|
||||
}
|
||||
|
||||
static void
|
||||
appkit_render_symbol (struct widget *widget)
|
||||
{
|
||||
appkit_render_padding (widget);
|
||||
|
||||
const struct xui_icon_point *icon =
|
||||
((const struct xui_symbol *) widget)->icon;
|
||||
|
||||
// TODO: There might be an attribute for symbols, to handle this better.
|
||||
NSColor *color = appkit_fg (widget);
|
||||
if (!(widget->attrs & A_BOLD))
|
||||
color = [color colorWithAlphaComponent:[color alphaComponent] * 0.5];
|
||||
|
||||
[color setFill];
|
||||
NSBezierPath *path = [NSBezierPath bezierPath];
|
||||
[path setWindingRule:NSWindingRuleEvenOdd];
|
||||
|
||||
int x = widget->x, y = widget->y + (widget->height - widget->width) / 2;
|
||||
bool started = false;
|
||||
for (const struct xui_icon_point *p = icon;
|
||||
!isinf (p[0].x) || !isinf (p[0].y)
|
||||
|| !isinf (p[1].x) || !isinf (p[1].y); p++)
|
||||
{
|
||||
if (isinf (p->x))
|
||||
{
|
||||
[path closePath];
|
||||
started = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
NSPoint point = NSMakePoint
|
||||
(x + p->x / 20.0 * widget->width,
|
||||
y + p->y / 20.0 * widget->width);
|
||||
if (!started)
|
||||
[path moveToPoint:point];
|
||||
else
|
||||
[path lineToPoint:point];
|
||||
started = true;
|
||||
}
|
||||
[path fill];
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
appkit_make_symbol (chtype attrs, const char *label,
|
||||
const struct xui_icon_point *icon)
|
||||
{
|
||||
struct widget *w =
|
||||
xui_new_symbol (appkit_make_label (attrs, 0, label), icon);
|
||||
if (icon)
|
||||
{
|
||||
w->on_render = appkit_render_symbol;
|
||||
|
||||
// It should be padded by the caller horizontally.
|
||||
w->height = g_xui.vunit;
|
||||
w->width = w->height * 3 / 4;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
static void
|
||||
appkit_render_gauge (struct widget *widget)
|
||||
{
|
||||
const struct xui_gauge *self = (const struct xui_gauge *) widget;
|
||||
|
||||
int part = self->fraction * widget->width;
|
||||
[appkit_fg_attrs (widget->attrs) setFill];
|
||||
NSRectFill (NSMakeRect (widget->x, widget->y, part, widget->height));
|
||||
[appkit_bg_attrs (widget->attrs) setFill];
|
||||
NSRectFill (NSMakeRect (widget->x + part, widget->y,
|
||||
widget->width - part, widget->height));
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
appkit_make_gauge (chtype attrs, double fraction)
|
||||
{
|
||||
struct xui_gauge *self = xcalloc (1, sizeof *self);
|
||||
self->widget.text = "";
|
||||
self->widget.on_render = appkit_render_gauge;
|
||||
self->widget.attrs = attrs;
|
||||
self->widget.width = -1;
|
||||
self->widget.height = g_xui.vunit;
|
||||
self->fraction = MIN (MAX (fraction, 0.), 1.);
|
||||
return &self->widget;
|
||||
}
|
||||
|
||||
static void
|
||||
appkit_render_scrollbar (struct widget *widget)
|
||||
{
|
||||
struct xui_scrollbar *self = (struct xui_scrollbar *) widget;
|
||||
appkit_render_padding (widget);
|
||||
|
||||
struct xui_scrollbar_part bar =
|
||||
xui_compute_scrollbar (self, widget->height, g_xui.vunit);
|
||||
|
||||
[appkit_fg_attrs (widget->attrs) setFill];
|
||||
NSRectFill (NSMakeRect
|
||||
(widget->x, widget->y + bar.start, widget->width, bar.length));
|
||||
}
|
||||
|
||||
static struct widget *
|
||||
appkit_make_scrollbar (chtype attrs, long top, long total)
|
||||
{
|
||||
struct xui_scrollbar *self = xcalloc (1, sizeof *self);
|
||||
self->widget.text = "";
|
||||
self->widget.on_render = appkit_render_scrollbar;
|
||||
self->widget.attrs = attrs;
|
||||
self->widget.width = g_xui.vunit / 2;
|
||||
self->top = top;
|
||||
self->total = total;
|
||||
return &self->widget;
|
||||
}
|
||||
|
||||
static void
|
||||
appkit_render_widget (struct widget *w, NSRect clip)
|
||||
{
|
||||
@@ -2215,6 +2690,12 @@ appkit_render_widget (struct widget *w, NSRect clip)
|
||||
appkit_render_widget (child, subclip);
|
||||
}
|
||||
|
||||
static void
|
||||
appkit_beep (void)
|
||||
{
|
||||
NSBeep ();
|
||||
}
|
||||
|
||||
static void
|
||||
appkit_render (void)
|
||||
{
|
||||
@@ -2269,7 +2750,11 @@ static struct ui appkit_ui =
|
||||
{
|
||||
.padding = appkit_make_padding,
|
||||
.label = appkit_make_label,
|
||||
.symbol = appkit_make_symbol,
|
||||
.gauge = appkit_make_gauge,
|
||||
.scrollbar = appkit_make_scrollbar,
|
||||
|
||||
.beep = appkit_beep,
|
||||
.render = appkit_render,
|
||||
.flip = appkit_flip,
|
||||
.destroy = appkit_destroy,
|
||||
@@ -2563,70 +3048,70 @@ appkit_send_focus_event (bool focused)
|
||||
- (void) keyDown:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_key_down (event))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) mouseDown:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event, TERMO_MOUSE_PRESS, 1))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) mouseUp:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event, TERMO_MOUSE_RELEASE, 1))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) rightMouseDown:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event, TERMO_MOUSE_PRESS, 3))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) rightMouseUp:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event, TERMO_MOUSE_RELEASE, 3))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) otherMouseDown:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event,
|
||||
TERMO_MOUSE_PRESS, appkit_mouse_button (event)))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) otherMouseUp:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event,
|
||||
TERMO_MOUSE_RELEASE, appkit_mouse_button (event)))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) mouseDragged:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event, TERMO_MOUSE_DRAG, 1))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) rightMouseDragged:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event, TERMO_MOUSE_DRAG, 3))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) otherMouseDragged:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_mouse_event (event,
|
||||
TERMO_MOUSE_DRAG, appkit_mouse_button (event)))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
- (void) scrollWheel:(NSEvent *)event
|
||||
{
|
||||
if (!on_appkit_scroll_event (event))
|
||||
NSBeep ();
|
||||
appkit_beep ();
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -2899,6 +3384,7 @@ static struct widget *
|
||||
xui_hbox (struct widget *head)
|
||||
{
|
||||
struct widget *self = xcalloc (1, sizeof *self);
|
||||
self->text = "";
|
||||
self->children = head;
|
||||
self->on_allocated = xui_on_hbox_allocated;
|
||||
|
||||
@@ -2952,6 +3438,7 @@ static struct widget *
|
||||
xui_vbox (struct widget *head)
|
||||
{
|
||||
struct widget *self = xcalloc (1, sizeof *self);
|
||||
self->text = "";
|
||||
self->children = head;
|
||||
self->on_allocated = xui_on_vbox_allocated;
|
||||
|
||||
@@ -3027,6 +3514,9 @@ xui_preinit (void)
|
||||
// Note that non-Unicode locales are handled pretty inefficiently.
|
||||
g_xui.locale_is_utf8 = !strcasecmp_ascii (locale_charset (), "UTF-8");
|
||||
|
||||
// It doesn't work 100% (e.g. incompatible with underlining in urxvt)
|
||||
g_xui.use_partial_boxes = g_xui.locale_is_utf8;
|
||||
|
||||
// Presumably, although not necessarily; unsure if queryable at all.
|
||||
g_xui.focused = true;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user