From dd73346aad190239ed133d0c4835dbaa05d1b0cd Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 23 Jul 2020 19:30:17 +0300 Subject: Entering URLs and updating the navbar --- src/main.c | 2 +- src/ui/documentwidget.c | 20 +++++++++----------- src/ui/inputwidget.c | 37 ++++++++++++++++++++++++++++++++----- src/ui/labelwidget.c | 10 +++++++--- src/ui/window.c | 39 ++++++++++++++++++++++++++++++++++++--- src/ui/window.h | 1 + 6 files changed, 86 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/main.c b/src/main.c index 457cc1f2..f6ff045d 100644 --- a/src/main.c +++ b/src/main.c @@ -15,7 +15,7 @@ int main(int argc, char **argv) { init_Foundation(); printf("Lagrange: A Beautiful Gemini Client\n"); /* Initialize SDL. */ - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { fprintf(stderr, "SDL init failed: %s\n", SDL_GetError()); return -1; } diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 9d95957a..71399d3b 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -301,6 +301,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e if (isResize_UserEvent(ev)) { setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); updateVisible_DocumentWidget_(d); + refresh_Widget(w); } else if (isCommand_Widget(w, ev, "scroll.moved")) { d->scrollY = arg_Command(command_UserEvent(ev)); @@ -410,30 +411,27 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { } static void draw_DocumentWidget_(const iDocumentWidget *d) { - const iWidget *w = constAs_Widget(d); + const iWidget *w = constAs_Widget(d); + const iRect bounds = bounds_Widget(w); draw_Widget(w); + iDrawContext ctx = { .widget = d, .bounds = documentBounds_DocumentWidget_(d) }; + init_Paint(&ctx.paint); + fillRect_Paint(&ctx.paint, bounds, gray25_ColorId); /* Update the document? */ if (!isEmpty_String(d->newSource)) { iDocumentWidget *m = iConstCast(iDocumentWidget *, d); /* TODO: Do this in the background. However, that requires a text metrics calculator that does not try to cache the glyph bitmaps. */ - setSource_GmDocument(m->doc, m->newSource, documentWidth_DocumentWidget_(m)); + setSource_GmDocument(m->doc, m->newSource, width_Rect(ctx.bounds)); + postCommandf_App("document.changed url:%s", cstr_String(d->url)); clear_String(m->newSource); m->scrollY = 0; m->state = ready_DocumentState; updateVisible_DocumentWidget_(m); } if (d->state != ready_DocumentState) return; - iDrawContext ctx = { .widget = d, .bounds = documentBounds_DocumentWidget_(d) }; - const iRect bounds = bounds_Widget(w); - init_Paint(&ctx.paint); - fillRect_Paint(&ctx.paint, bounds, gray25_ColorId); setClip_Paint(&ctx.paint, bounds); - render_GmDocument( - d->doc, - visibleRange_DocumentWidget_(d), - drawRun_DrawContext_, - &ctx); + render_GmDocument(d->doc, visibleRange_DocumentWidget_(d), drawRun_DrawContext_, &ctx); clearClip_Paint(&ctx.paint); draw_Widget(w); } diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 1a692907..b73fac9e 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c @@ -5,6 +5,8 @@ #include #include +static const int REFRESH_INTERVAL = 256; + struct Impl_InputWidget { iWidget widget; enum iInputMode mode; @@ -14,6 +16,7 @@ struct Impl_InputWidget { size_t cursor; int font; iClick click; + uint32_t timer; }; iDefineObjectConstructionArgs(InputWidget, (size_t maxLen), maxLen) @@ -33,9 +36,13 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue); } init_Click(&d->click, d, SDL_BUTTON_LEFT); + d->timer = 0; } void deinit_InputWidget(iInputWidget *d) { + if (d->timer) { + SDL_RemoveTimer(d->timer); + } deinit_Array(&d->oldText); deinit_Array(&d->text); } @@ -68,6 +75,7 @@ void setText_InputWidget(iInputWidget *d, const iString *text) { iConstForEach(String, i, text) { pushBack_Array(&d->text, &i.value); } + refresh_Widget(as_Widget(d)); } void setTextCStr_InputWidget(iInputWidget *d, const char *cstr) { @@ -80,6 +88,11 @@ void setCursor_InputWidget(iInputWidget *d, size_t pos) { d->cursor = iMin(pos, size_Array(&d->text)); } +static uint32_t refreshTimer_(uint32_t interval, void *d) { + refresh_Widget(d); + return interval; +} + void begin_InputWidget(iInputWidget *d) { iWidget *w = as_Widget(d); if (flags_Widget(w) & selected_WidgetFlag) { @@ -96,6 +109,8 @@ void begin_InputWidget(iInputWidget *d) { } SDL_StartTextInput(); setFlags_Widget(w, selected_WidgetFlag, iTrue); + refresh_Widget(w); + d->timer = SDL_AddTimer(REFRESH_INTERVAL, refreshTimer_, d); } void end_InputWidget(iInputWidget *d, iBool accept) { @@ -107,19 +122,23 @@ void end_InputWidget(iInputWidget *d, iBool accept) { if (!accept) { setCopy_Array(&d->text, &d->oldText); } + SDL_RemoveTimer(d->timer); + d->timer = 0; SDL_StopTextInput(); setFlags_Widget(w, selected_WidgetFlag, iFalse); const char *id = cstr_String(id_Widget(as_Widget(d))); if (!*id) id = "_"; + refresh_Widget(w); postCommand_Widget(w, "input.ended id:%s arg:%d", id, accept ? 1 : 0); } static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { - if (isCommand_Widget(as_Widget(d), ev, "focus.gained")) { + iWidget *w = as_Widget(d); + if (isCommand_Widget(w, ev, "focus.gained")) { begin_InputWidget(d); return iTrue; } - else if (isCommand_Widget(as_Widget(d), ev, "focus.lost")) { + else if (isCommand_Widget(w, ev, "focus.lost")) { end_InputWidget(d, iTrue); return iTrue; } @@ -139,7 +158,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { return iTrue; } const size_t curMax = iMin(size_Array(&d->text), d->maxLen - 1); - if (ev->type == SDL_KEYDOWN && isFocused_Widget(as_Widget(d))) { + if (ev->type == SDL_KEYDOWN && isFocused_Widget(w)) { const int key = ev->key.keysym.sym; const int mods = keyMods_Sym(ev->key.keysym.mod); switch (key) { @@ -159,28 +178,33 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { else if (d->cursor > 0) { remove_Array(&d->text, --d->cursor); } + refresh_Widget(w); return iTrue; case SDLK_d: if (mods != KMOD_CTRL) break; case SDLK_DELETE: if (d->cursor < size_Array(&d->text)) { remove_Array(&d->text, d->cursor); + refresh_Widget(w); } return iTrue; case SDLK_k: if (mods == KMOD_CTRL) { removeN_Array(&d->text, d->cursor, size_Array(&d->text) - d->cursor); + refresh_Widget(w); return iTrue; } break; case SDLK_HOME: case SDLK_END: d->cursor = (key == SDLK_HOME ? 0 : curMax); + refresh_Widget(w); return iTrue; case SDLK_a: case SDLK_e: if (mods == KMOD_CTRL) { d->cursor = (key == 'a' ? 0 : curMax); + refresh_Widget(w); return iTrue; } break; @@ -191,6 +215,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { else if (d->cursor > 0) { d->cursor--; } + refresh_Widget(w); return iTrue; case SDLK_RIGHT: if (mods & KMOD_PRIMARY) { @@ -199,6 +224,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { else if (d->cursor < curMax) { d->cursor++; } + refresh_Widget(w); return iTrue; case SDLK_TAB: /* Allow focus switching. */ @@ -209,7 +235,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { } return iTrue; } - else if (ev->type == SDL_TEXTINPUT && isFocused_Widget(as_Widget(d))) { + else if (ev->type == SDL_TEXTINPUT && isFocused_Widget(w)) { const iString *uni = collectNewCStr_String(ev->text.text); const iChar chr = first_String(uni); if (d->mode == insert_InputMode) { @@ -225,9 +251,10 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { setFocus_Widget(NULL); } } + refresh_Widget(w); return iTrue; } - return processEvent_Widget(as_Widget(d), ev); + return processEvent_Widget(w, ev); } static void draw_InputWidget_(const iInputWidget *d) { diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 7b64857b..341c2f0e 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -30,19 +30,23 @@ static void trigger_LabelWidget_(const iLabelWidget *d) { } static iBool processEvent_LabelWidget_(iLabelWidget *d, const SDL_Event *ev) { + iWidget *w = &d->widget; if (isCommand_UserEvent(ev, "metrics.changed")) { updateSize_LabelWidget(d); } if (!isEmpty_String(&d->command)) { switch (processEvent_Click(&d->click, ev)) { case started_ClickResult: - setFlags_Widget(&d->widget, pressed_WidgetFlag, iTrue); + setFlags_Widget(w, pressed_WidgetFlag, iTrue); + refresh_Widget(w); return iTrue; case aborted_ClickResult: - setFlags_Widget(&d->widget, pressed_WidgetFlag, iFalse); + setFlags_Widget(w, pressed_WidgetFlag, iFalse); + refresh_Widget(w); return iTrue; case finished_ClickResult: - setFlags_Widget(&d->widget, pressed_WidgetFlag, iFalse); + setFlags_Widget(w, pressed_WidgetFlag, iFalse); + refresh_Widget(w); trigger_LabelWidget_(d); return iTrue; case double_ClickResult: diff --git a/src/ui/window.c b/src/ui/window.c index 241cd3b0..43233010 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -49,6 +49,10 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) { } return iTrue; } + else if (equal_Command(cmd, "setfocus")) { + setFocus_Widget(findWidget_App(cstr_String(string_Command(cmd, "id")))); + return iTrue; + } else if (handleCommand_App(cmd)) { return iTrue; } @@ -70,6 +74,23 @@ static const iMenuItem editMenuItems[] = { static const iMenuItem viewMenuItems[] = { }; +static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { + if (equal_Command(cmd, "input.ended")) { + iInputWidget *url = findChild_Widget(navBar, "url"); + if (arg_Command(cmd) && pointer_Command(cmd) == url) { + postCommandf_App("open url:%s", cstr_String(text_InputWidget(url))); + return iTrue; + } + } + else if (equal_Command(cmd, "document.changed")) { + iInputWidget *url = findWidget_App("url"); + setTextCStr_InputWidget(url, valuePtr_Command(cmd, "url")); + setTitle_Window(get_Window(), text_InputWidget(url)); + return iTrue; + } + return iFalse; +} + static void setupUserInterface_Window(iWindow *d) { /* Children of root cover the entire window. */ setFlags_Widget(d->root, resizeChildren_WidgetFlag, iTrue); @@ -87,12 +108,14 @@ static void setupUserInterface_Window(iWindow *d) { arrangeHorizontal_WidgetFlag, iTrue); addChild_Widget(div, iClob(navBar)); - setBackgroundColor_Widget(div, gray25_ColorId); + setCommandHandler_Widget(navBar, handleNavBarCommands_); + setBackgroundColor_Widget(navBar, gray25_ColorId); addChild_Widget(navBar, iClob(new_LabelWidget("Back", 0, 0, "navigate.back"))); addChild_Widget(navBar, iClob(new_LabelWidget("Fwd", 0, 0, "navigate.forward"))); addChild_Widget(navBar, iClob(new_LabelWidget("Home", 0, 0, "navigate.home"))); iInputWidget *url = new_InputWidget(0); + setId_Widget(as_Widget(url), "url"); setTextCStr_InputWidget(url, "gemini://"); addChildFlags_Widget(navBar, iClob(url), expand_WidgetFlag); } @@ -250,6 +273,7 @@ static void setupUserInterface_Window(iWindow *d) { #endif /* Glboal keyboard shortcuts. */ { // addAction_Widget(d->root, SDLK_LEFTBRACKET, KMOD_SHIFT | KMOD_PRIMARY, "tabs.prev"); + addAction_Widget(d->root, 'l', KMOD_PRIMARY, "setfocus id:url"); } } @@ -381,8 +405,13 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { widget = mouseGrab_Widget(); } } - /* TODO: Auto-refresh when hover widget changes. */ - return dispatchEvent_Widget(widget, &event); + iWidget *oldHover = hover_Widget(); + /* Dispatch the event to the tree of widgets. */ + iBool wasUsed = dispatchEvent_Widget(widget, &event); + if (oldHover != hover_Widget()) { + postRefresh_App(); + } + return wasUsed; } } return iFalse; @@ -432,6 +461,10 @@ void resize_Window(iWindow *d, int w, int h) { updateRootSize_Window_(d); } +void setTitle_Window(iWindow *d, const iString *title) { + SDL_SetWindowTitle(d->win, cstr_String(title)); +} + void setUiScale_Window(iWindow *d, float uiScale) { uiScale = iClamp(uiScale, 0.5f, 4.0f); if (d) { diff --git a/src/ui/window.h b/src/ui/window.h index d0413af4..70e52751 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -23,6 +23,7 @@ struct Impl_Window { iBool processEvent_Window (iWindow *, const SDL_Event *); void draw_Window (iWindow *); void resize_Window (iWindow *, int w, int h); +void setTitle_Window (iWindow *, const iString *title); void setUiScale_Window (iWindow *, float uiScale); iInt2 rootSize_Window (const iWindow *); -- cgit v1.2.3