From 62ccfe59ae63858dc5c21039e907a9277134d171 Mon Sep 17 00:00:00 2001
From: Rudy Dellomas III <dther@dther.xyz>
Date: Sat, 20 Apr 2024 03:17:14 +1000
Subject: [PATCH 1/3] Add Lua function to Win for directly editing cell styling
 by position

---
 ui-terminal.c | 15 +++++++++++++++
 ui.h          |  1 +
 vis-lua.c     | 27 +++++++++++++++++++++++++++
 3 files changed, 43 insertions(+)

diff --git a/ui-terminal.c b/ui-terminal.c
index dd04f7613..6ec1e413c 100644
--- a/ui-terminal.c
+++ b/ui-terminal.c
@@ -69,6 +69,9 @@ struct UiTermWin {
 #include "ui-terminal-vt100.c"
 #endif
 
+/* helper macro for handling UiTerm.cells */
+#define CELL_AT_POS(UI, X, Y) (((UI)->cells) + (X) + ((Y) * (UI)->width));
+
 __attribute__((noreturn)) static void ui_die(Ui *ui, const char *msg, va_list ap) {
 	UiTerm *tui = (UiTerm*)ui;
 	ui_term_backend_free(tui);
@@ -305,6 +308,17 @@ static void ui_window_style_set(UiWin *w, Cell *cell, enum UiStyle id) {
 	memcpy(&cell->style, &set, sizeof(CellStyle));
 }
 
+static bool ui_window_style_set_pos(UiWin *w, int x, int y, enum UiStyle id) {
+	UiTermWin *win = (UiTermWin*)w;
+	UiTerm *tui = win->ui;
+	if (x < 0 || y < 0 || y >= win->height || x >= win->width) {
+		return false;
+	}
+	Cell *cell = CELL_AT_POS(tui, win->x + x, win->y + y)
+	ui_window_style_set(w, cell, id);
+	return true;
+}
+
 static void ui_window_status(UiWin *w, const char *status) {
 	UiTermWin *win = (UiTermWin*)w;
 	if (!(win->options & UI_OPTION_STATUSBAR))
@@ -534,6 +548,7 @@ static UiWin *ui_window_new(Ui *ui, Win *w, enum UiOption options) {
 
 	win->uiwin = (UiWin) {
 		.style_set = ui_window_style_set,
+		.style_set_pos = ui_window_style_set_pos,
 		.status = ui_window_status,
 		.options_set = ui_window_options_set,
 		.options_get = ui_window_options_get,
diff --git a/ui.h b/ui.h
index 76bdcce33..45b6065eb 100644
--- a/ui.h
+++ b/ui.h
@@ -97,6 +97,7 @@ struct Ui {
 
 struct UiWin {
 	void (*style_set)(UiWin*, Cell*, enum UiStyle);
+	bool (*style_set_pos)(UiWin*, int x, int y, enum UiStyle);
 	void (*status)(UiWin*, const char *txt);
 	void (*options_set)(UiWin*, enum UiOption);
 	enum UiOption (*options_get)(UiWin*);
diff --git a/vis-lua.c b/vis-lua.c
index a3029fab4..3d80293ab 100644
--- a/vis-lua.c
+++ b/vis-lua.c
@@ -2106,6 +2106,32 @@ static int window_style(lua_State *L) {
 	return 0;
 }
 
+/***
+ * Style the single terminal cell at the given coordinates, relative to this window.
+ *
+ * Completely independent of the file buffer, and can be used to style UI elements,
+ * such as the status bar.
+ * The style will be cleared after every window redraw.
+ * @function style_pos
+ * @tparam int id display style registered with @{style_define}
+ * @tparam int x 0-based x coordinate within Win, where (0,0) is the top left corner
+ * @tparam int y See above
+ * @treturn bool false if the coordinates would be outside the window's dimensions
+ * @see style_define
+ * @usage
+ * win:style_pos(win.STYLE_COLOR_COLUMN, 0, win.height - 1)
+ * -- Styles the first character of the status bar (or the last line, if disabled)
+ */
+static int window_style_pos(lua_State *L) {
+	Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
+	enum UiStyle style = luaL_checkunsigned(L, 2);
+	size_t x = checkpos(L, 3);
+	size_t y = checkpos(L, 4);
+	bool ret = win->ui->style_set_pos(win->ui, (int)x, (int)y, style);
+	lua_pushboolean(L, ret);
+	return 1;
+}
+
 /***
  * Set window status line.
  *
@@ -2175,6 +2201,7 @@ static const struct luaL_Reg window_funcs[] = {
 	{ "unmap", window_unmap },
 	{ "style_define", window_style_define },
 	{ "style", window_style },
+	{ "style_pos", window_style_pos },
 	{ "status", window_status },
 	{ "draw", window_draw },
 	{ "close", window_close },

From 596ec20dd81fc4a48a528bfed9e241343ee03eaa Mon Sep 17 00:00:00 2001
From: Rudy Dellomas III <dther@dther.xyz>
Date: Sun, 21 Apr 2024 20:25:40 +1000
Subject: [PATCH 2/3] Emit an event (ui_draw) immediately before drawing the
 screen

This allows better control over styling, as well as potential for
entirely new UI elements implemented entirely using the Lua API.
---
 lua/vis.lua   |  2 ++
 main.c        |  1 +
 ui-terminal.c |  1 +
 vis-core.h    |  1 +
 vis-lua.c     | 14 +++++++++++++-
 vis-lua.h     |  1 +
 vis.c         |  4 ++++
 vis.h         |  1 +
 8 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/lua/vis.lua b/lua/vis.lua
index 32fb2a12b..5473f17df 100644
--- a/lua/vis.lua
+++ b/lua/vis.lua
@@ -167,6 +167,7 @@ local events = {
 	WIN_STATUS = "Event::WIN_STATUS", -- see @{win_status}
 	TERM_CSI = "Event::TERM_CSI", -- see @{term_csi}
 	PROCESS_RESPONSE = "Event::PROCESS_RESPONSE", -- see @{process_response}
+	UI_DRAW = "Event::UI_DRAW", -- see @{ui_draw}
 }
 
 events.file_close = function(...) events.emit(events.FILE_CLOSE, ...) end
@@ -183,6 +184,7 @@ events.win_open = function(...) events.emit(events.WIN_OPEN, ...) end
 events.win_status = function(...) events.emit(events.WIN_STATUS, ...) end
 events.term_csi = function(...) events.emit(events.TERM_CSI, ...) end
 events.process_response = function(...) events.emit(events.PROCESS_RESPONSE, ...) end
+events.ui_draw = function(...) events.emit(events.UI_DRAW, ...) end
 
 local handlers = {}
 
diff --git a/main.c b/main.c
index 8e5ad4287..8aa31c6b8 100644
--- a/main.c
+++ b/main.c
@@ -2229,6 +2229,7 @@ int main(int argc, char *argv[]) {
 		.win_highlight = vis_lua_win_highlight,
 		.win_status = vis_lua_win_status,
 		.term_csi = vis_lua_term_csi,
+		.ui_draw = vis_lua_ui_draw,
 	};
 
 	vis = vis_new(ui_term_new(), &event);
diff --git a/ui-terminal.c b/ui-terminal.c
index 6ec1e413c..09e618ea8 100644
--- a/ui-terminal.c
+++ b/ui-terminal.c
@@ -392,6 +392,7 @@ static void ui_draw(Ui *ui) {
 		ui_window_draw((UiWin*)win);
 	if (tui->info[0])
 		ui_draw_string(tui, 0, tui->height-1, tui->info, NULL, UI_STYLE_INFO);
+	vis_event_emit(tui->vis, VIS_EVENT_UI_DRAW);
 	ui_term_backend_blit(tui);
 }
 
diff --git a/vis-core.h b/vis-core.h
index 8d3980cd9..4f81e4cbd 100644
--- a/vis-core.h
+++ b/vis-core.h
@@ -236,6 +236,7 @@ enum VisEvents {
 	VIS_EVENT_WIN_HIGHLIGHT,
 	VIS_EVENT_WIN_STATUS,
 	VIS_EVENT_TERM_CSI,
+	VIS_EVENT_UI_DRAW,
 };
 
 bool vis_event_emit(Vis*, enum VisEvents, ...);
diff --git a/vis-lua.c b/vis-lua.c
index 3d80293ab..91ee42894 100644
--- a/vis-lua.c
+++ b/vis-lua.c
@@ -166,7 +166,7 @@ void vis_lua_win_status(Vis *vis, Win *win) { window_status_update(vis, win); }
 void vis_lua_term_csi(Vis *vis, const long *csi) { }
 void vis_lua_process_response(Vis *vis, const char *name,
                               char *buffer, size_t len, ResponseType rtype) { }
-
+void vis_lua_ui_draw(Vis *vis) { }
 
 #else
 
@@ -3689,4 +3689,16 @@ void vis_lua_process_response(Vis *vis, const char *name,
 	lua_pop(L, 1);
 }
 
+/***
+ * Emitted immediately before the UI is drawn to the screen.
+ * Allows last-minute overrides to the styling of UI elements.
+ *
+ * *WARNING:* This is emitted every screen draw!
+ * Use sparingly and check for `nil` values!
+ * @function ui_draw
+ */
+void vis_lua_ui_draw(Vis *vis) {
+	vis_lua_event_call(vis, "ui_draw");
+}
+
 #endif
diff --git a/vis-lua.h b/vis-lua.h
index d622ff542..b4f3f51bb 100644
--- a/vis-lua.h
+++ b/vis-lua.h
@@ -42,5 +42,6 @@ void vis_lua_win_highlight(Vis*, Win*);
 void vis_lua_win_status(Vis*, Win*);
 void vis_lua_term_csi(Vis*, const long *);
 void vis_lua_process_response(Vis *, const char *, char *, size_t, ResponseType);
+void vis_lua_ui_draw(Vis*);
 
 #endif
diff --git a/vis.c b/vis.c
index e28740539..0c05558a1 100644
--- a/vis.c
+++ b/vis.c
@@ -105,6 +105,10 @@ bool vis_event_emit(Vis *vis, enum VisEvents id, ...) {
 		if (vis->event->term_csi)
 			vis->event->term_csi(vis, va_arg(ap, const long *));
 		break;
+	case VIS_EVENT_UI_DRAW:
+		if (vis->event->ui_draw)
+			vis->event->ui_draw(vis);
+		break;
 	}
 
 	va_end(ap);
diff --git a/vis.h b/vis.h
index b67d3c5e0..8287a45a5 100644
--- a/vis.h
+++ b/vis.h
@@ -58,6 +58,7 @@ typedef struct {
 	void (*win_highlight)(Vis*, Win*);
 	void (*win_status)(Vis*, Win*);
 	void (*term_csi)(Vis*, const long *);
+	void (*ui_draw)(Vis*);
 } VisEvent;
 
 /** Union used to pass arguments to key action functions. */

From 1d37e5c8930c7542855da0a864c02ee4fd88c092 Mon Sep 17 00:00:00 2001
From: Rudy Dellomas III <dther@dther.xyz>
Date: Wed, 24 Apr 2024 19:12:28 +1000
Subject: [PATCH 3/3] lua: Serve viewport dimensions in viewport table

These values are useful for calculating terminal positions.
---
 vis-lua.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/vis-lua.c b/vis-lua.c
index 91ee42894..a62daf898 100644
--- a/vis-lua.c
+++ b/vis-lua.c
@@ -1833,8 +1833,10 @@ static const struct luaL_Reg registers_funcs[] = {
  * Viewport currently being displayed.
  * Changing these values will not move the viewport.
  * @table viewport
- * @tfield Range bytes
- * @tfield Range lines
+ * @tfield Range bytes file bytes, from 0, at the start and end of the viewport
+ * @tfield Range lines file lines, from 1, at the top and bottom of the viewport
+ * @tfield int height lines in viewport, accounting for window decoration
+ * @tfield int width columns in viewport, accounting for window decoration
  */
 /***
  * The window width.
@@ -1875,13 +1877,19 @@ static int window_index(lua_State *L) {
 			l.start = view_lines_first(win->view)->lineno;
 			l.end = view_lines_last(win->view)->lineno;
 
-			lua_createtable(L, 0, 2);
+			lua_createtable(L, 0, 4);
 			lua_pushstring(L, "bytes");
 			pushrange(L, &b);
 			lua_settable(L, -3);
 			lua_pushstring(L, "lines");
 			pushrange(L, &l);
 			lua_settable(L, -3);
+			lua_pushstring(L, "width");
+			lua_pushunsigned(L, view_width_get(win->view));
+			lua_settable(L, -3);
+			lua_pushstring(L, "height");
+			lua_pushunsigned(L, view_height_get(win->view));
+			lua_settable(L, -3);
 			return 1;
 		}