aboutsummaryrefslogtreecommitdiff
path: root/libui
diff options
context:
space:
mode:
authorJulian Hurst <julian.hurst92@gmail.com>2022-05-16 00:35:21 +0200
committerJulian Hurst <julian.hurst92@gmail.com>2022-05-16 00:35:21 +0200
commit9740eee555cac43cc27e08a39a38e09a96ecb002 (patch)
tree555c0fd89906364a8f527c8f1c72ab81b8303adf /libui
parent35639332a5dc8e9b26e8299c999940a9b6ca2ddb (diff)
downloadilhare-9740eee555cac43cc27e08a39a38e09a96ecb002.tar.gz
Add layout and a common widget type
Diffstat (limited to 'libui')
-rw-r--r--libui/README1
-rw-r--r--libui/layout/layout.ha42
-rw-r--r--libui/libui.ha6
-rw-r--r--libui/widget/README2
-rw-r--r--libui/widget/list/README1
-rw-r--r--libui/widget/list/list.ha (renamed from libui/list/list.ha)45
-rw-r--r--libui/widget/widget.ha63
7 files changed, 147 insertions, 13 deletions
diff --git a/libui/README b/libui/README
new file mode 100644
index 0000000..df8992f
--- /dev/null
+++ b/libui/README
@@ -0,0 +1 @@
+The libui module provides widgets for creating a tui (terminal user interface).
diff --git a/libui/layout/layout.ha b/libui/layout/layout.ha
new file mode 100644
index 0000000..ef6bf71
--- /dev/null
+++ b/libui/layout/layout.ha
@@ -0,0 +1,42 @@
+use libui::widget;
+use io;
+use unix::tty;
+use fmt;
+use os;
+
+export type layout = struct {
+ widgets: []*widget::widget,
+};
+
+// Create and return a new layout from a list of widgets. [[finishall]] must be
+// called to properly free the widget's nad layout's resources.
+export fn newlayout(widgets: *widget::widget...) layout = {
+ return layout {
+ widgets = widgets,
+ };
+};
+
+// Display all the widgets contained in the given layout.
+export fn print(layout: layout) (void | widget::error) = {
+ for (let i = 0z; i < len(layout.widgets); i += 1) {
+ match (layout.widgets[i].print) {
+ case null =>
+ return;
+ case let f: *widget::print =>
+ f(layout.widgets[i])?;
+ };
+ };
+};
+
+// Finish and free the widgets in the given layout.
+export fn finishall(layout: *layout) void = {
+ for (let i = 0z; i < len(layout.widgets); i += 1) {
+ match (layout.widgets[i].finish) {
+ case null =>
+ return;
+ case let f: *widget::finish =>
+ f(layout.widgets[i]);
+ };
+ };
+ free(layout.widgets);
+};
diff --git a/libui/libui.ha b/libui/libui.ha
index 6ad33d1..7e40a9d 100644
--- a/libui/libui.ha
+++ b/libui/libui.ha
@@ -26,7 +26,7 @@ export fn init() ttyui = {
fmt::fatal(tty::strerror(e));
};
if (!tty::isatty(f)) {
- fmt::fatal("/dev/tty is not a tty");
+ fmt::fatal("stream is not a tty");
};
let term = match (tty::termios_query(f)) {
case let t: tty::termios =>
@@ -74,6 +74,7 @@ export fn scan(ui: ttyui) (rune | utf8::invalid | io::EOF | io::error) = {
};
// Notify (call) the ttyui's listeners with the ttyui and r as a parameter.
+// Returns true if a listener returned true, false otherwise.
export fn notify(ui: *ttyui, r: rune) bool = {
for (let i = 0z; i < len(ui.listeners); i += 1) {
if (ui.listeners[i](ui, r)) {
@@ -110,7 +111,8 @@ export fn addlistener(ui: *ttyui, l: listener) void = {
// Print a string or rune to the ttyui.
export fn print(ui: ttyui, arg: (str | rune)) void = {
- fmt::fprintf(ui.f, "{}\r", arg)!;
+ fmt::fprint(ui.f, arg)!;
+ //fmt::fprintf(ui.f, "{}\r", arg)!;
};
// Clear the ttyui.
diff --git a/libui/widget/README b/libui/widget/README
new file mode 100644
index 0000000..ae62628
--- /dev/null
+++ b/libui/widget/README
@@ -0,0 +1,2 @@
+This module contains functions common to all widgets and provides a base for
+implementing custom widget types (print, finish, ...).
diff --git a/libui/widget/list/README b/libui/widget/list/README
new file mode 100644
index 0000000..c8f71db
--- /dev/null
+++ b/libui/widget/list/README
@@ -0,0 +1 @@
+libui::list provides a list widget that supports line truncating, scrolling, item marking (contains, fnmatch and regex) and searching. Multiple convenient navigation functions are provided such as [[up]], [[down]], [[top]] and [[bottom]].
diff --git a/libui/list/list.ha b/libui/widget/list/list.ha
index 7b181c4..57239f3 100644
--- a/libui/list/list.ha
+++ b/libui/widget/list/list.ha
@@ -1,4 +1,5 @@
use libui;
+use libui::widget;
use fmt;
use os;
use strings;
@@ -11,11 +12,12 @@ use wcwidth;
use set;
export type listwidget = struct {
+ widget: widget::widget,
ui: libui::ttyui,
items: []str,
marked: set::set,
cursor: size,
- listeners: []listener,
+ //listeners: []listener,
frame: frame,
sz: ttysize,
};
@@ -31,6 +33,10 @@ export type ttysize = struct {
cols: u16,
};
+// An input listener on a list widget. The returning value is intended to be
+// used as a signal that will be returned by [[notify]] in order to trigger
+// certain more global ui events (terminate the program, change widget focus,
+// etc.). To register a listener with a listwidget, use [[addlistener]].
export type listener = *fn(l: *listwidget, r: rune) bool;
// Create a new list with the given items.
@@ -42,11 +48,16 @@ export fn newlist(ui: libui::ttyui, items: str...) listwidget = {
yield len(items);
};
let w = listwidget {
+ widget = widget::widget {
+ print = &print,
+ finish = &finish,
+ ...
+ },
ui = ui,
items = items,
marked = set::set {...},
cursor = 0z,
- listeners = [],
+ //listeners = [],
frame = frame {
start = 0u16,
end = rows: u16,
@@ -59,14 +70,24 @@ export fn newlist(ui: libui::ttyui, items: str...) listwidget = {
return w;
};
-// Add a listener to the given list.
-export fn addlistener(list: *listwidget, l: listener) void = {
- append(list.listeners, l);
+// Free the list's items, marked items and call the common widget finish
+// function [[widget::finishcommon]].
+export fn finish(list: *widget::widget) void = {
+ const list = list: *listwidget;
+ free(list.items);
+ set::finish(&list.marked);
+ widget::finishcommon(list);
};
+// Add a listener to the given list.
+//export fn addlistener(list: *listwidget, l: listener) void = {
+ //append(list.listeners, l);
+//};
+
// Print the list's items while truncating the items to not be wider than the
// list.sz.cols.
-export fn print(list: *listwidget) (void | io::error | tty::error) = {
+export fn print(list: *widget::widget) (void | widget::error) = {
+ const list = list: *listwidget;
//let sz = libui::getwinsize(list.ui)?;
//let rows: (u16 | size) = if (sz.rows - 2 < len(list.items)) {
//yield sz.rows - 2;
@@ -98,16 +119,18 @@ export fn print(list: *listwidget) (void | io::error | tty::error) = {
};
strio::concat(&st, "\r\n")?;
};
+ // unsupported?
+ //io::copy(list.ui.f, &st)?;
let s = strio::string(&st);
- defer free(s);
libui::print(list.ui, s);
+ io::close(&st)?;
};
// Notify (call) the listwidget's listeners with the listwidget and r as a
-// parameter.
+// parameter. Returns true if a listener returned true, false otherwise.
export fn notify(l: *listwidget, r: rune) bool = {
- for (let i = 0z; i < len(l.listeners); i += 1) {
- if (l.listeners[i](l, r)) {
+ for (let i = 0z; i < len(l.widget.listeners); i += 1) {
+ if (l.widget.listeners[i](l, r)) {
return true;
};
};
@@ -213,7 +236,7 @@ export fn containsmark(l: *listwidget, s: str) void = {
};
};
-// Marks items based on fnmatch (globbing).
+// Marks items based on fnmatch (globbing syntax).
export fn fnmatchmark(l: *listwidget, s: str) void = {
for (let i = 0z; i < len(l.items); i += 1) {
if (fnmatch::fnmatch(s, l.items[i])) {
diff --git a/libui/widget/widget.ha b/libui/widget/widget.ha
new file mode 100644
index 0000000..1f23aac
--- /dev/null
+++ b/libui/widget/widget.ha
@@ -0,0 +1,63 @@
+use io;
+use unix::tty;
+
+export type error = !(io::error | tty::error);
+
+// A function that displays the widget.
+export type print = fn(w: *widget) (void | error);
+
+// A function that frees the resources associated to the widget.
+export type finish = fn(w: *widget) void;
+
+// An input listener on a widget. The returning value is intended to be used as
+// a signal that will be returned by [[notify]] in order to trigger certain more
+// global ui events (terminate the program, change widget focus, etc.). To
+// register a listener with a widget, use [[addlistener]].
+export type listener = *fn(w: *widget, r: rune) bool;
+
+// A widget is an abstraction around a user-defined UI component. Custom widgets
+// can be created through sub-typing:
+//
+// export type my_widget = struct {
+// widget: widget::widget,
+// lines: []str,
+// };
+//
+// fn print(w: *widget::widget) (void | widget::error) = {
+// const w = w: *my_widget;
+// for (let i = 0z; i < len(w.lines); i += 1) {
+// fmt::println(w.lines[i])!;
+// };
+// };
+//
+// fn finish(w: *widget::widget) void = {
+// const w = w: *my_widget;
+// free(w.lines);
+// widget::finishcommon(w);
+// };
+//
+// let list = my_widget {
+// widget = widget::widget {
+// print = &print,
+// finish = &finish,
+// ...
+// };
+// lines = strings::split("one,two,three", ","),
+// };
+// let l = layout::newlayout(list);
+// layout::print(l)!;
+export type widget = struct {
+ print: nullable *print,
+ finish: nullable *finish,
+ listeners: []listener,
+};
+
+// Add a listener to the given widget.
+export fn addlistener(w: *widget, listener: listener) void = {
+ append(w.listeners, listener);
+};
+
+// Free the widget's listeners.
+export fn finishcommon(w: *widget) void = {
+ free(w.listeners);
+};