aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.def.h16
-rw-r--r--main.c64
2 files changed, 78 insertions, 2 deletions
diff --git a/config.def.h b/config.def.h
index 16102aa..505d064 100644
--- a/config.def.h
+++ b/config.def.h
@@ -11,6 +11,7 @@
extern void tabopen(Cinnamon* cinnamon);
extern void tabclose(Cinnamon* cinnamon);
extern void set_mode(Cinnamon* cinnamon, int new_mode);
+extern void inject_hint_mode(Cinnamon* cinnamon, void (*callback)(Cinnamon*, char hint_buffer[16]));
/* guard ensures config exists/enables brower */
#define CINNAMON_ENABLED
@@ -31,6 +32,16 @@ extern void set_mode(Cinnamon* cinnamon, int new_mode);
/* Keybind definitions */
/* we use the _kbd prefix here just because, idk, good practice to keep keybinds isolated? in practice use whatever the hell you want as long as it doesnt interfere with builtin functions */
+
+static void _conf_hint_callback(Cinnamon* cinnamon, char hint_buffer[16]) {
+ if (strlen(hint_buffer) > 0) {
+ GtkWidget *webview = gtk_notebook_get_nth_page(GTK_NOTEBOOK(cinnamon->notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(cinnamon->notebook)));
+ char js[512];
+ snprintf(js, sizeof(js), "(function() {if (window.__cinnamon_link_map && window.__cinnamon_link_map['%s']) { let el = window.__cinnamon_link_map['%s']; let tag = el.tagName.toLowerCase(); if (tag === 'input' || tag === 'textarea' || tag === 'select') { el.focus(); } else { el.click(); } }})();", hint_buffer, hint_buffer);
+ webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(webview), js, NULL, NULL, NULL);
+ }
+}
+
extern bool cmdbar_focused;
static void _kbd_insmode(Cinnamon* cinnamon, void* arg) {
set_mode(cinnamon, 1);
@@ -110,6 +121,10 @@ static void _kbd_forward(Cinnamon* cinnamon, void* arg) {
webkit_web_view_go_forward(WEBKIT_WEB_VIEW(webview));
}
+static void _kbd_follow(Cinnamon* cinnamon, void* arg) {
+ inject_hint_mode(cinnamon, &_conf_hint_callback);
+}
+
static const Keybind keybinds[] = {
{ "t", &_kbd_tabopen, NULL },
{ "d", &_kbd_tabclose, NULL },
@@ -122,6 +137,7 @@ static const Keybind keybinds[] = {
{ "h", &_kbd_scroll, "left" },
{ "l", &_kbd_scroll, "right" },
{ "r", &_kbd_reload, NULL },
+ { "f", &_kbd_follow, NULL },
{ "<S-h>", &_kbd_back, NULL },
{ "<S-l>", &_kbd_forward, NULL },
{ "<S-r>", &_kbd_hardreload, NULL },
diff --git a/main.c b/main.c
index 2374d61..c59603a 100644
--- a/main.c
+++ b/main.c
@@ -27,9 +27,15 @@ typedef struct {
#include "config.h"
-/* 0 = normal mode, 1 = insert, 2 = passthrough */
+/* 0 = normal mode, 1 = insert, 2 = passthrough, 3 = hint */
static int mode = 0;
bool cmdbar_focused = false;
+static char hint_buffer[16] = {0};
+static int hint_mode_active = 0;
+void (*hint_callback)(Cinnamon* cinnamon, char hint_buffer[16]);
+
+/* some forward declarations */
+void cancel_hint_mode(Cinnamon* cinnamon);
static void parse_keybind(const char *key, guint *keyval, GdkModifierType *mods) {
char name[32];
@@ -93,7 +99,41 @@ static gboolean on_cmdbar_activate(GtkEntry *entry, gpointer data) {
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) {
Cinnamon* cinnamon = (Cinnamon*)data;
GdkModifierType relevant = GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK;
- if (mode == 1 || mode == 2) {
+ if (mode == 3) {
+ if (event->keyval >= GDK_KEY_0 && event->keyval <= GDK_KEY_9) {
+ char digit = '0' + (event->keyval - GDK_KEY_0);
+ int len = strlen(hint_buffer);
+ if (len < 15) {
+ hint_buffer[len] = digit;
+ hint_buffer[len+1] = '\0';
+
+ char buf[32];
+ snprintf(buf, sizeof(buf), "-- HINT MODE -- [%s]", hint_buffer);
+ gtk_label_set_text(GTK_LABEL(cinnamon->indicator), buf);
+ }
+ return TRUE;
+ }
+ else if (event->keyval == GDK_KEY_BackSpace) {
+ int len = strlen(hint_buffer);
+ if (len > 0) {
+ hint_buffer[len-1] = '\0';
+ char buf[32];
+ snprintf(buf, sizeof(buf), "-- HINT MODE -- [%s]", hint_buffer);
+ gtk_label_set_text(GTK_LABEL(cinnamon->indicator), buf);
+ }
+ return TRUE;
+ }
+ else if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
+ hint_callback(cinnamon, hint_buffer);
+ cancel_hint_mode(cinnamon);
+ return TRUE;
+ }
+ else if (event->keyval == GDK_KEY_Escape) {
+ cancel_hint_mode(cinnamon);
+ return TRUE;
+ }
+ }
+ else if (mode == 1 || mode == 2) {
if (mode == 1 && event->keyval == GDK_KEY_Escape) {
set_mode(cinnamon, 0);
if (cmdbar_focused) {
@@ -140,6 +180,7 @@ void set_mode(Cinnamon* cinnamon, int new_mode) {
case 0: gtk_label_set_text(GTK_LABEL(cinnamon->indicator), "-- NORMAL --"); break;
case 1: gtk_label_set_text(GTK_LABEL(cinnamon->indicator), "-- INSERT --"); break;
case 2: gtk_label_set_text(GTK_LABEL(cinnamon->indicator), "-- PASSTHROUGH --"); break;
+ case 3: gtk_label_set_text(GTK_LABEL(cinnamon->indicator), "-- HINT MODE -- [start typing...]"); break;
}
}
@@ -170,6 +211,25 @@ void tabclose(Cinnamon* cinnamon) {
}
}
+void inject_hint_mode(Cinnamon* cinnamon, void (*callback)(Cinnamon*, char hint_buffer[16])) {
+ if (mode == 3) return;
+ hint_callback = callback;
+ GtkWidget *webview = gtk_notebook_get_nth_page(GTK_NOTEBOOK(cinnamon->notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(cinnamon->notebook)));
+ memset(hint_buffer, 0, sizeof(hint_buffer));
+ set_mode(cinnamon, 3);
+ const char* js = "(function() { if (window.__cinnamon_hints) return; window.__cinnamon_hints = true; window.__cinnamon_link_map = {}; let links = Array.from(document.querySelectorAll('a, button, [role=button], input:not([type=\"hidden\"]), textarea, select, [contenteditable=\"true\"]')); links.forEach((el, idx) => { let num = (idx + 1).toString(); let rect = el.getBoundingClientRect(); let div = document.createElement('div'); div.textContent = num; div.className = '__cinnamon_hint'; div.style.position = 'fixed'; div.style.left = rect.left + 'px'; div.style.top = (rect.top - 20) + 'px'; div.style.backgroundColor = '#fcd24d'; div.style.border = '1px solid #1e1e2f'; div.style.padding = '2px 4px'; div.style.fontSize = '12px'; div.style.fontWeight = 'bold'; div.style.zIndex = '999999'; div.style.borderRadius = '3px'; div.style.color = '#1e1e2f'; document.body.appendChild(div); window.__cinnamon_link_map[num] = el; }); })();";
+ webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(webview), js, NULL, NULL, NULL);
+}
+
+void cancel_hint_mode(Cinnamon* cinnamon) {
+ GtkWidget *webview = gtk_notebook_get_nth_page(GTK_NOTEBOOK(cinnamon->notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(cinnamon->notebook)));
+ const char* js = "(function() { document.querySelectorAll('.__cinnamon_hint').forEach(el => el.remove()); delete window.__cinnamon_hints; delete window.__cinnamon_link_map; })();";
+ webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(webview), js, NULL, NULL, NULL);
+
+ set_mode(cinnamon, 0);
+ memset(hint_buffer, 0, sizeof(hint_buffer));
+}
+
int main(int argc, char *argv[]) {
#ifndef CINNAMON_ENABLED
return 1;