From 1e8b17b775715edfc68157d9be6743109f65a71b Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 11 Aug 2020 16:10:42 +0300 Subject: Document outline; jumping to clicked heading --- src/ui/sidebarwidget.c | 227 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 202 insertions(+), 25 deletions(-) (limited to 'src/ui/sidebarwidget.c') diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 8f1273bf..46b36434 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -1,57 +1,234 @@ #include "sidebarwidget.h" #include "labelwidget.h" #include "scrollwidget.h" +#include "documentwidget.h" #include "paint.h" #include "util.h" +#include "command.h" +#include "../gmdocument.h" +#include "app.h" + +#include + +iDeclareType(SidebarItem) + +struct Impl_SidebarItem { + int indent; + iChar icon; + iString label; + iString meta; + iString url; + size_t index; +}; + +void init_SidebarItem(iSidebarItem *d) { + d->indent = 0; + d->icon = 0; + d->index = 0; + init_String(&d->label); + init_String(&d->meta); + init_String(&d->url); +} + +void deinit_SidebarItem(iSidebarItem *d) { + deinit_String(&d->url); + deinit_String(&d->meta); + deinit_String(&d->label); +} + +iDefineTypeConstruction(SidebarItem) + +/*----------------------------------------------------------------------------------------------*/ struct Impl_SidebarWidget { iWidget widget; enum iSidebarMode mode; iScrollWidget *scroll; + int scrollY; + iLabelWidget *modeButtons[max_SidebarMode]; + int itemHeight; + iArray items; + size_t hoverItem; + iClick click; }; iDefineObjectConstruction(SidebarWidget) +static void clearItems_SidebarWidget_(iSidebarWidget *d) { + iForEach(Array, i, &d->items) { + deinit_SidebarItem(i.value); + } + clear_Array(&d->items); +} + +static void updateItems_SidebarWidget_(iSidebarWidget *d) { + clearItems_SidebarWidget_(d); + switch (d->mode) { + case documentOutline_SidebarMode: { + const iGmDocument *doc = document_DocumentWidget(document_App()); + iConstForEach(Array, i, headings_GmDocument(doc)) { + const iGmHeading *head = i.value; + iSidebarItem item; + init_SidebarItem(&item); + item.index = index_ArrayConstIterator(&i); + setRange_String(&item.label, head->text); + item.indent = head->level * 4 * gap_UI; + pushBack_Array(&d->items, &item); + } + break; + } + case bookmarks_SidebarMode: + break; + case history_SidebarMode: + break; + default: + break; + } + refresh_Widget(as_Widget(d)); +} + +void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { + if (d->mode == mode) return; + d->mode = mode; + for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { + setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); + } + const float heights[max_SidebarMode] = { 1.2f, 2, 3, 3 }; + d->itemHeight = heights[mode] * lineHeight_Text(default_FontId); +} + void init_SidebarWidget(iSidebarWidget *d) { iWidget *w = as_Widget(d); init_Widget(w); - setFlags_Widget(w, resizeChildren_WidgetFlag, iTrue); - d->mode = documentOutline_SidebarMode; - addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); - w->rect.size.x = 80 * gap_UI; + setBackgroundColor_Widget(w, none_ColorId); + setFlags_Widget(w, hover_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue); + d->scrollY = 0; + d->mode = -1; + w->rect.size.x = 100 * gap_UI; + init_Array(&d->items, sizeof(iSidebarItem)); + d->hoverItem = iInvalidPos; + init_Click(&d->click, d, SDL_BUTTON_LEFT); setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); - iWidget *modeButtons = makeHDiv_Widget(); - setFlags_Widget(modeButtons, arrangeWidth_WidgetFlag | arrangeHeight_WidgetFlag, iTrue); - addChild_Widget(w, iClob(modeButtons)); - addChildFlags_Widget( - modeButtons, - iClob(new_LabelWidget( - "\U0001f5b9 Outline", 0, 0, format_CStr("sidebar.mode arg:%d", documentOutline_SidebarMode))), frameless_WidgetFlag); - addChildFlags_Widget( - modeButtons, - iClob(new_LabelWidget( - "\U0001f588 Bookmarks", 0, 0, format_CStr("sidebar.mode arg:%d", bookmarks_SidebarMode))), frameless_WidgetFlag); - addChildFlags_Widget( - modeButtons, - iClob(new_LabelWidget( - "\U0001f553 History", 0, 0, format_CStr("sidebar.mode arg:%d", history_SidebarMode))), frameless_WidgetFlag); - addChildFlags_Widget( - modeButtons, - iClob(new_LabelWidget( - "\U0001f464 Identities", 0, 0, format_CStr("sidebar.mode arg:%d", identities_SidebarMode))), frameless_WidgetFlag); + const char *buttonLabels[max_SidebarMode] = { + "\U0001f5b9 Outline", + "\U0001f588 Bookmarks", + "\U0001f553 History", + "\U0001f464 Identities", + }; + for (int i = 0; i < max_SidebarMode; i++) { + d->modeButtons[i] = addChildFlags_Widget( + w, + iClob(new_LabelWidget(buttonLabels[i], 0, 0, format_CStr("sidebar.mode arg:%d", i))), + frameless_WidgetFlag); + } + addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); + setThumb_ScrollWidget(d->scroll, 0, 0); + setMode_SidebarWidget(d, documentOutline_SidebarMode); } void deinit_SidebarWidget(iSidebarWidget *d) { - iUnused(d); + clearItems_SidebarWidget_(d); + deinit_Array(&d->items); +} + +static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { + iRect bounds = bounds_Widget(constAs_Widget(d)); + adjustEdges_Rect(&bounds, as_Widget(d->modeButtons[0])->rect.size.y + gap_UI, 0, 0, 0); + return bounds; +} + +static size_t itemIndex_SidebarWidget_(const iSidebarWidget *d, iInt2 pos) { + const iRect bounds = contentBounds_SidebarWidget_(d); + pos.y -= top_Rect(bounds); + if (pos.y < 0) return iInvalidPos; + size_t index = pos.y / d->itemHeight; + if (index >= size_Array(&d->items)) return iInvalidPos; + return index; +} + +static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { + const iSidebarItem *item = constAt_Array(&d->items, index); + switch (d->mode) { + case documentOutline_SidebarMode: { + const iGmDocument *doc = document_DocumentWidget(document_App()); + const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->index); + postCommandf_App("document.goto loc:%p", head->text.start); + break; + } + } } static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { iWidget *w = as_Widget(d); + /* Handle commands. */ + if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { + const char *cmd = command_UserEvent(ev); + if (isCommand_Widget(w, ev, "sidebar.mode")) { + setMode_SidebarWidget(d, arg_Command(cmd)); + updateItems_SidebarWidget_(d); + return iTrue; + } + else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { + updateItems_SidebarWidget_(d); + } + } + if (ev->type == SDL_MOUSEMOTION) { + const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); + size_t hover = iInvalidPos; + if (contains_Widget(w, mouse)) { + hover = itemIndex_SidebarWidget_(d, mouse); + + } + if (hover != d->hoverItem) { + d->hoverItem = hover; + refresh_Widget(w); + } + } + switch (processEvent_Click(&d->click, ev)) { + case started_ClickResult: + refresh_Widget(w); + break; + case finished_ClickResult: + if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && + d->hoverItem != iInvalidSize) { + //printf("click:%zu\n", d->hoverItem); fflush(stdout); + itemClicked_SidebarWidget_(d, d->hoverItem); + } + refresh_Widget(w); + break; + default: + break; + } return processEvent_Widget(w, ev); } static void draw_SidebarWidget_(const iSidebarWidget *d) { - const iWidget *w = constAs_Widget(d); + const iWidget *w = constAs_Widget(d); + const iRect bounds = contentBounds_SidebarWidget_(d); + const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); + iPaint p; + init_Paint(&p); + /* Draw the items. */ { + iInt2 pos = topLeft_Rect(bounds); + const int font = default_FontId; + iConstForEach(Array, i, &d->items) { + const iSidebarItem *item = i.value; + const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; + const iBool isHover = (d->hoverItem == index_ArrayConstIterator(&i)); + if (isHover) { + fillRect_Paint(&p, itemRect, isPressing ? orange_ColorId : teal_ColorId); + } + setClip_Paint(&p, itemRect); + const int fg = isHover ? (isPressing ? black_ColorId : white_ColorId) : gray75_ColorId; + if (d->mode == documentOutline_SidebarMode) { + drawRange_Text(font, init_I2(pos.x + 3 * gap_UI + item->indent, + mid_Rect(itemRect).y - lineHeight_Text(font) / 2), + fg, range_String(&item->label)); + } + unsetClip_Paint(&p); + pos.y += d->itemHeight; + } + } draw_Widget(w); } -- cgit v1.2.3