From 8e2828efc6d9c88f39592f4ae1c709937b8b9f46 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sat, 5 Sep 2020 10:11:10 +0300 Subject: Refactor: ListWidget for a general purpose list The sidebar uses a ListWidget now, but the layout needs fixing. --- src/ui/listwidget.c | 323 ++++++++++++++++++++++++++ src/ui/listwidget.h | 69 ++++++ src/ui/sidebarwidget.c | 607 ++++++++++++++++++++++++++++--------------------- src/ui/util.c | 4 + src/ui/util.h | 1 + src/ui/widget.c | 6 + 6 files changed, 755 insertions(+), 255 deletions(-) create mode 100644 src/ui/listwidget.c create mode 100644 src/ui/listwidget.h (limited to 'src') diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c new file mode 100644 index 00000000..3865becc --- /dev/null +++ b/src/ui/listwidget.c @@ -0,0 +1,323 @@ +/* Copyright 2020 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "listwidget.h" +#include "scrollwidget.h" +#include "paint.h" +#include "util.h" +#include "command.h" + +#include + +void init_ListItem(iListItem *d) { + d->isSeparator = iFalse; + d->isSelected = iFalse; +} + +void deinit_ListItem(iListItem *d) { + iUnused(d); +} + +iDefineObjectConstruction(ListItem) +iDefineClass(ListItem) + +/*----------------------------------------------------------------------------------------------*/ + +iDefineObjectConstruction(ListWidget) + +struct Impl_ListWidget { + iWidget widget; + iScrollWidget *scroll; + int scrollY; + int itemHeight; + iPtrArray items; + size_t hoverItem; + iClick click; + iIntSet invalidItems; + SDL_Texture *visBuffer; + iBool visBufferValid; +}; + +void init_ListWidget(iListWidget *d) { + iWidget *w = as_Widget(d); + init_Widget(w); + setId_Widget(w, "list"); + setFlags_Widget(w, hover_WidgetFlag, iTrue); + addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); + setThumb_ScrollWidget(d->scroll, 0, 0); + d->scrollY = 0; + init_PtrArray(&d->items); + d->hoverItem = iInvalidPos; + init_Click(&d->click, d, SDL_BUTTON_LEFT); + init_IntSet(&d->invalidItems); + d->visBuffer = NULL; + d->visBufferValid = iFalse; +} + +void deinit_ListWidget(iListWidget *d) { + clear_ListWidget(d); + deinit_PtrArray(&d->items); + SDL_DestroyTexture(d->visBuffer); +} + +void invalidate_ListWidget(iListWidget *d) { + d->visBufferValid = iFalse; + clear_IntSet(&d->invalidItems); /* all will be drawn */ + refresh_Widget(as_Widget(d)); +} + +void clear_ListWidget(iListWidget *d) { + iForEach(PtrArray, i, &d->items) { + deref_Object(i.ptr); + } + clear_PtrArray(&d->items); + d->hoverItem = iInvalidPos; +} + +void addItem_ListWidget(iListWidget *d, iAnyObject *item) { + pushBack_PtrArray(&d->items, ref_Object(item)); +} + +iScrollWidget *scroll_ListWidget(iListWidget *d) { + return d->scroll; +} + +size_t numItems_ListWidget(const iListWidget *d) { + return size_PtrArray(&d->items); +} + +static int scrollMax_ListWidget_(const iListWidget *d) { + return iMax(0, + (int) size_PtrArray(&d->items) * d->itemHeight - + height_Rect(innerBounds_Widget(constAs_Widget(d)))); +} + +void updateVisible_ListWidget(iListWidget *d) { + const int contentSize = size_PtrArray(&d->items) * d->itemHeight; + const iRect bounds = innerBounds_Widget(as_Widget(d)); + setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_ListWidget_(d) }); + setThumb_ScrollWidget(d->scroll, + d->scrollY, + contentSize > 0 ? height_Rect(bounds_Widget(as_Widget(d->scroll))) * + height_Rect(bounds) / contentSize + : 0); +} + +void setItemHeight_ListWidget(iListWidget *d, int itemHeight) { + d->itemHeight = itemHeight; + invalidate_ListWidget(d); +} + +int scrollPos_ListWidget(const iListWidget *d) { + return d->scrollY; +} + +void setScrollPos_ListWidget(iListWidget *d, int pos) { + d->scrollY = pos; + d->hoverItem = iInvalidPos; + invalidate_ListWidget(d); +} + +void scrollOffset_ListWidget(iListWidget *d, int offset) { + const int oldScroll = d->scrollY; + d->scrollY += offset; + if (d->scrollY < 0) { + d->scrollY = 0; + } + const int scrollMax = scrollMax_ListWidget_(d); + d->scrollY = iMin(d->scrollY, scrollMax); + if (oldScroll != d->scrollY) { + d->hoverItem = iInvalidPos; + updateVisible_ListWidget(d); + invalidate_ListWidget(d); + } +} + +static int visCount_ListWidget_(const iListWidget *d) { + return iMin(height_Rect(innerBounds_Widget(constAs_Widget(d))) / d->itemHeight, + (int) size_PtrArray(&d->items)); +} + +static iRanges visRange_ListWidget_(const iListWidget *d) { + iRanges vis = { d->scrollY / d->itemHeight, 0 }; + vis.end = iMin(size_PtrArray(&d->items), vis.start + visCount_ListWidget_(d)); + return vis; +} + +size_t itemIndex_ListWidget(const iListWidget *d, iInt2 pos) { + const iRect bounds = innerBounds_Widget(constAs_Widget(d)); + pos.y -= top_Rect(bounds) - d->scrollY; + if (pos.y < 0) return iInvalidPos; + size_t index = pos.y / d->itemHeight; + if (index >= size_Array(&d->items)) return iInvalidPos; + return index; +} + +const iAnyObject *constHoverItem_ListWidget(const iListWidget *d) { + if (d->hoverItem < size_PtrArray(&d->items)) { + return constAt_PtrArray(&d->items, d->hoverItem); + } + return NULL; +} + +iAnyObject *hoverItem_ListWidget(iListWidget *d) { + if (d->hoverItem < size_PtrArray(&d->items)) { + return at_PtrArray(&d->items, d->hoverItem); + } + return NULL; +} + +static void setHoverItem_ListWidget_(iListWidget *d, size_t index) { + if (index < size_PtrArray(&d->items)) { + const iListItem *item = at_PtrArray(&d->items, index); + if (item->isSeparator) { + index = iInvalidPos; + } + } + if (d->hoverItem != index) { + insert_IntSet(&d->invalidItems, d->hoverItem); + insert_IntSet(&d->invalidItems, index); + d->hoverItem = index; + refresh_Widget(as_Widget(d)); + } +} + +void updateMouseHover_ListWidget(iListWidget *d) { + const iInt2 mouse = mouseCoord_Window(get_Window()); + setHoverItem_ListWidget_(d, itemIndex_ListWidget(d, mouse)); +} + +static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { + iWidget *w = as_Widget(d); + if (isCommand_SDLEvent(ev)) { + const char *cmd = command_UserEvent(ev); + if (equal_Command(cmd, "theme.changed")) { + invalidate_ListWidget(d); + } + else if (isCommand_Widget(w, ev, "scroll.moved")) { + setScrollPos_ListWidget(d, arg_Command(cmd)); + return iTrue; + } + } + if (ev->type == SDL_MOUSEMOTION) { + const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); + size_t hover = iInvalidPos; + if (!contains_Widget(constAs_Widget(d->scroll), mouse) && + contains_Widget(w, mouse)) { + hover = itemIndex_ListWidget(d, mouse); + } + setHoverItem_ListWidget_(d, hover); + } + if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { +#if defined (iPlatformApple) + /* Momentum scrolling. */ + scrollOffset_ListWidget(d, -ev->wheel.y * get_Window()->pixelRatio); +#else + scrollOffset_ListWidget(d, -ev->wheel.y * 3 * d->itemHeight); +#endif + return iTrue; + } + switch (processEvent_Click(&d->click, ev)) { + case started_ClickResult: + //invalidate_SidebarWidget_(d); + break; + case finished_ClickResult: + if (contains_Rect(innerBounds_Widget(w), pos_Click(&d->click)) && + d->hoverItem != iInvalidSize) { + //itemClicked_SidebarWidget_(d, d->hoverItem); + postCommand_Widget(w, "list.clicked arg:%zu item:%p", + d->hoverItem, constHoverItem_ListWidget(d)); + } + // invalidate_SidebarWidget_(d); + break; + default: + break; + } + return processEvent_Widget(w, ev); +} + +static void allocVisBuffer_ListWidget_(iListWidget *d) { + const iInt2 size = innerBounds_Widget(as_Widget(d)).size; + if (!d->visBuffer || !isEqual_I2(size_SDLTexture(d->visBuffer), size)) { + if (d->visBuffer) { + SDL_DestroyTexture(d->visBuffer); + } + d->visBuffer = SDL_CreateTexture(renderer_Window(get_Window()), + SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, + size.x, + size.y); + SDL_SetTextureBlendMode(d->visBuffer, SDL_BLENDMODE_NONE); + d->visBufferValid = iFalse; + } +} + +static void draw_ListWidget_(const iListWidget *d) { + const iWidget *w = constAs_Widget(d); + const iRect bounds = innerBounds_Widget(w); + draw_Widget(w); /* background */ + iPaint p; + init_Paint(&p); + if (!d->visBufferValid || !isEmpty_IntSet(&d->invalidItems)) { + iListWidget *m = iConstCast(iListWidget *, d); + allocVisBuffer_ListWidget_(m); + beginTarget_Paint(&p, d->visBuffer); + const iRect bufBounds = (iRect){ zero_I2(), bounds.size }; + if (!d->visBufferValid) { + fillRect_Paint(&p, bufBounds, w->bgColor); + } + /* Draw items. */ { + const iRanges visRange = visRange_ListWidget_(d); + iInt2 pos = init_I2(0, -(d->scrollY % d->itemHeight)); + for (size_t i = visRange.start; i < visRange.end; i++) { + /* TODO: Refactor to loop through invalidItems only. */ + if (!d->visBufferValid || contains_IntSet(&d->invalidItems, i)) { + const iListItem *item = constAt_PtrArray(&d->items, i); + const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; + setClip_Paint(&p, intersect_Rect(itemRect, bufBounds)); + if (d->visBufferValid) { + fillRect_Paint(&p, itemRect, w->bgColor); + } + class_ListItem(item)->draw(item, &p, itemRect, d); + unsetClip_Paint(&p); + } + pos.y += d->itemHeight; + } + } + endTarget_Paint(&p); + /* Update state. */ + m->visBufferValid = iTrue; + clear_IntSet(&m->invalidItems); + } + SDL_RenderCopy( + renderer_Window(get_Window()), d->visBuffer, NULL, (const SDL_Rect *) &bounds); +} + +iBool isMouseDown_ListWidget(const iListWidget *d) { + return d->click.isActive && + contains_Rect(innerBounds_Widget(constAs_Widget(d)), pos_Click(&d->click)); +} + +iBeginDefineSubclass(ListWidget, Widget) + .processEvent = (iAny *) processEvent_ListWidget_, + .draw = (iAny *) draw_ListWidget_, +iEndDefineSubclass(ListWidget) diff --git a/src/ui/listwidget.h b/src/ui/listwidget.h new file mode 100644 index 00000000..38bdebc9 --- /dev/null +++ b/src/ui/listwidget.h @@ -0,0 +1,69 @@ +/* Copyright 2020 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#pragma once + +#include "scrollwidget.h" +#include "paint.h" + +#include + +iDeclareType(ListWidget) + +iBeginDeclareClass(ListItem) + void (*draw) (const iAnyObject *, iPaint *p, iRect rect, const iListWidget *list); +iEndDeclareClass(ListItem) + +iDeclareType(ListItem) + + struct Impl_ListItem { + iObject object; + iBool isSeparator; + iBool isSelected; +}; + +iDeclareObjectConstruction(ListItem) + +iDeclareWidgetClass(ListWidget) +iDeclareObjectConstruction(ListWidget) + +void setItemHeight_ListWidget(iListWidget *, int itemHeight); + +void invalidate_ListWidget (iListWidget *); +void clear_ListWidget (iListWidget *); +void addItem_ListWidget (iListWidget *, iAnyObject *item); + +iScrollWidget * scroll_ListWidget (iListWidget *); + +int scrollPos_ListWidget (const iListWidget *); + +void setScrollPos_ListWidget (iListWidget *, int pos); +void scrollOffset_ListWidget (iListWidget *, int offset); +void updateVisible_ListWidget (iListWidget *); +void updateMouseHover_ListWidget (iListWidget *); + +size_t numItems_ListWidget (const iListWidget *); +size_t itemIndex_ListWidget(const iListWidget *, iInt2 pos); +const iAnyObject *constHoverItem_ListWidget(const iListWidget *); +iAnyObject *hoverItem_ListWidget(iListWidget *); + +iBool isMouseDown_ListWidget (const iListWidget *); diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index a919fca6..8dada9eb 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gmdocument.h" #include "inputwidget.h" #include "labelwidget.h" +#include "listwidget.h" #include "paint.h" #include "scrollwidget.h" #include "util.h" @@ -41,27 +42,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include iDeclareType(SidebarItem) +typedef iListItemClass iSidebarItemClass; struct Impl_SidebarItem { - uint32_t id; - int indent; - iChar icon; - iString label; - iString meta; - iString url; - iBool isSeparator; - iBool isSelected; + iListItem listItem; + uint32_t id; + int indent; + iChar icon; + iString label; + iString meta; + iString url; }; void init_SidebarItem(iSidebarItem *d) { + init_ListItem(&d->listItem); d->id = 0; d->indent = 0; d->icon = 0; init_String(&d->label); init_String(&d->meta); init_String(&d->url); - d->isSeparator = iFalse; - d->isSelected = iFalse; } void deinit_SidebarItem(iSidebarItem *d) { @@ -70,53 +70,60 @@ void deinit_SidebarItem(iSidebarItem *d) { deinit_String(&d->label); } -iDefineTypeConstruction(SidebarItem) +static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, const iListWidget *list); + +iBeginDefineSubclass(SidebarItem, ListItem) + .draw = (iAny *) draw_SidebarItem_, +iEndDefineSubclass(SidebarItem) + +iDefineObjectConstruction(SidebarItem) /*----------------------------------------------------------------------------------------------*/ struct Impl_SidebarWidget { iWidget widget; enum iSidebarMode mode; - iScrollWidget *scroll; - int scrollY; + iListWidget *list; +// iScrollWidget *scroll; +// int scrollY; int modeScroll[max_SidebarMode]; int width; iLabelWidget *modeButtons[max_SidebarMode]; int itemHeight; int maxButtonLabelWidth; - iArray items; - size_t hoverItem; - iClick click; +// iArray items; +// size_t hoverItem; +// iClick click; iWidget *resizer; SDL_Cursor *resizeCursor; iWidget *menu; - iIntSet invalidItems; - SDL_Texture *visBuffer; - iBool visBufferValid; +// iIntSet invalidItems; +// SDL_Texture *visBuffer; +// iBool visBufferValid; }; iDefineObjectConstruction(SidebarWidget) -static void invalidate_SidebarWidget_(iSidebarWidget *d) { - d->visBufferValid = iFalse; - refresh_Widget(as_Widget(d)); - clear_IntSet(&d->invalidItems); /* all will be drawn */ -} +//static void invalidate_SidebarWidget_(iSidebarWidget *d) { +// d->visBufferValid = iFalse; +// refresh_Widget(as_Widget(d)); +// clear_IntSet(&d->invalidItems); /* all will be drawn */ +//} static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; } -static void clearItems_SidebarWidget_(iSidebarWidget *d) { - iForEach(Array, i, &d->items) { - deinit_SidebarItem(i.value); - } - clear_Array(&d->items); -} +//static void clearItems_SidebarWidget_(iSidebarWidget *d) { +// iForEach(Array, i, &d->items) { +// deinit_SidebarItem(i.value); +// } +// clear_Array(&d->items); +//} static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { iRect bounds = bounds_Widget(constAs_Widget(d)); - const iWidget *scroll = constAs_Widget(d->scroll); + const iWidget *scroll = constAs_Widget(scroll_ListWidget(d->list)); adjustEdges_Rect(&bounds, as_Widget(d->modeButtons[0])->rect.size.y + gap_UI, isVisible_Widget(scroll) ? -scroll->rect.size.x : 0, @@ -125,57 +132,60 @@ static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { return bounds; } -static int scrollMax_SidebarWidget_(const iSidebarWidget *d) { - return iMax(0, - (int) size_Array(&d->items) * d->itemHeight - - height_Rect(contentBounds_SidebarWidget_(d))); -} +//static int scrollMax_SidebarWidget_(const iSidebarWidget *d) { +// return iMax(0, +// (int) numItems_ListWidget(d->list) * d->itemHeight - +// height_Rect(contentBounds_SidebarWidget_(d))); +//} -static void updateVisible_SidebarWidget_(iSidebarWidget *d) { - const int contentSize = size_Array(&d->items) * d->itemHeight; - const iRect bounds = contentBounds_SidebarWidget_(d); - setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_SidebarWidget_(d) }); - setThumb_ScrollWidget(d->scroll, - d->scrollY, - contentSize > 0 ? height_Rect(bounds_Widget(as_Widget(d->scroll))) * - height_Rect(bounds) / contentSize - : 0); -} +//static void updateVisible_SidebarWidget_(iSidebarWidget *d) { +// const int contentSize = size_Array(&d->items) * d->itemHeight; +// const iRect bounds = contentBounds_SidebarWidget_(d); +// setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_SidebarWidget_(d) }); +// setThumb_ScrollWidget(d->scroll, +// d->scrollY, +// contentSize > 0 ? height_Rect(bounds_Widget(as_Widget(d->scroll))) * +// height_Rect(bounds) / contentSize +// : 0); +//} static int cmpTitle_Bookmark_(const iBookmark **a, const iBookmark **b) { return cmpStringCase_String(&(*a)->title, &(*b)->title); } static void updateItems_SidebarWidget_(iSidebarWidget *d) { - clearItems_SidebarWidget_(d); +// clearItems_SidebarWidget_(d); + clear_ListWidget(d->list); destroy_Widget(d->menu); d->menu = NULL; - d->hoverItem = iInvalidPos; +// d->hoverItem = iInvalidPos; 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.id = index_ArrayConstIterator(&i); - setRange_String(&item.label, head->text); - item.indent = head->level * 4 * gap_UI; - pushBack_Array(&d->items, &item); + iSidebarItem *item = new_SidebarItem(); +// init_SidebarItem(&item); + item->id = index_ArrayConstIterator(&i); + setRange_String(&item->label, head->text); + item->indent = head->level * 4 * gap_UI; + addItem_ListWidget(d->list, item); + iRelease(item); } break; } case bookmarks_SidebarMode: { iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), NULL, cmpTitle_Bookmark_)) { const iBookmark *bm = i.ptr; - iSidebarItem item; - init_SidebarItem(&item); - item.id = id_Bookmark(bm); - item.icon = bm->icon; - set_String(&item.url, &bm->url); - set_String(&item.label, &bm->title); - set_String(&item.meta, &bm->tags); - pushBack_Array(&d->items, &item); + iSidebarItem *item = new_SidebarItem(); +// init_SidebarItem(&item); + item->id = id_Bookmark(bm); + item->icon = bm->icon; + set_String(&item->url, &bm->url); + set_String(&item->label, &bm->title); + set_String(&item->meta, &bm->tags); + addItem_ListWidget(d->list, item); + iRelease(item); } d->menu = makeMenu_Widget( as_Widget(d), @@ -192,27 +202,32 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { const int thisYear = on.year; iConstForEach(PtrArray, i, list_Visited(visited_App(), 200)) { const iVisitedUrl *visit = i.ptr; - iSidebarItem item; - init_SidebarItem(&item); - set_String(&item.url, &visit->url); + iSidebarItem *item = new_SidebarItem(); +// init_SidebarItem(&item); + set_String(&item->url, &visit->url); iDate date; init_Date(&date, &visit->when); if (date.day != on.day || date.month != on.month || date.year != on.year) { on = date; /* Date separator. */ - iSidebarItem sep; - init_SidebarItem(&sep); - sep.isSeparator = iTrue; - set_String(&sep.meta, + iSidebarItem *sep = new_SidebarItem(); +// init_SidebarItem(&sep); + sep->listItem.isSeparator = iTrue; + set_String(&sep->meta, collect_String(format_Date( &date, date.year != thisYear ? "%b %d %Y" : "%b %d"))); - pushBack_Array(&d->items, &sep); +// pushBack_Array(&d->items, &sep); + addItem_ListWidget(d->list, sep); + iRelease(sep); /* Date separators are two items tall. */ - init_SidebarItem(&sep); - sep.isSeparator = iTrue; - pushBack_Array(&d->items, &sep); + sep = new_SidebarItem(); + sep->listItem.isSeparator = iTrue; + addItem_ListWidget(d->list, sep); + iRelease(sep); } - pushBack_Array(&d->items, &item); +// pushBack_Array(&d->items, &item); + addItem_ListWidget(d->list, item); + iRelease(item); } d->menu = makeMenu_Widget( as_Widget(d), @@ -230,16 +245,16 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { const iString *tabUrl = url_DocumentWidget(document_App()); iConstForEach(PtrArray, i, identities_GmCerts(certs_App())) { const iGmIdentity *ident = i.ptr; - iSidebarItem item; - init_SidebarItem(&item); - item.id = index_PtrArrayConstIterator(&i); - item.icon = ident->icon; - set_String(&item.label, collect_String(subject_TlsCertificate(ident->cert))); + iSidebarItem *item = new_SidebarItem(); +// init_SidebarItem(&item); + item->id = index_PtrArrayConstIterator(&i); + item->icon = ident->icon; + set_String(&item->label, collect_String(subject_TlsCertificate(ident->cert))); iDate until; validUntil_TlsCertificate(ident->cert, &until); const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl); format_String( - &item.meta, + &item->meta, "%s", isActive ? "Using" : isUsed_GmIdentity(ident) @@ -250,17 +265,19 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { ? "Temporary" : cstrCollect_String(format_Date(&until, "Expires %b %d, %Y")); if (isEmpty_String(&ident->notes)) { - appendFormat_String(&item.meta, "\n%s", expiry); + appendFormat_String(&item->meta, "\n%s", expiry); } else { - appendFormat_String(&item.meta, + appendFormat_String(&item->meta, " \u2014 %s\n%s%s", expiry, escape_Color(uiHeading_ColorId), cstr_String(&ident->notes)); } - item.isSelected = isActive; - pushBack_Array(&d->items, &item); + item->listItem.isSelected = isActive; + //pushBack_Array(&d->items, &item); + addItem_ListWidget(d->list, item); + iRelease(item); } const iMenuItem menuItems[] = { { "Use on This Page", 0, 0, "ident.use arg:1" }, @@ -280,39 +297,38 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { default: break; } - updateVisible_SidebarWidget_(d); - invalidate_SidebarWidget_(d); + updateVisible_ListWidget(d->list); + invalidate_ListWidget(d->list); } -static void scroll_SidebarWidget_(iSidebarWidget *d, int offset) { - const int oldScroll = d->scrollY; - d->scrollY += offset; - if (d->scrollY < 0) { - d->scrollY = 0; - } - const int scrollMax = scrollMax_SidebarWidget_(d); - d->scrollY = iMin(d->scrollY, scrollMax); - if (oldScroll != d->scrollY) { - d->hoverItem = iInvalidPos; - updateVisible_SidebarWidget_(d); - invalidate_SidebarWidget_(d); - } -} +//static void scroll_SidebarWidget_(iSidebarWidget *d, int offset) { +// const int oldScroll = d->scrollY; +// d->scrollY += offset; +// if (d->scrollY < 0) { +// d->scrollY = 0; +// } +// const int scrollMax = scrollMax_SidebarWidget_(d); +// d->scrollY = iMin(d->scrollY, scrollMax); +// if (oldScroll != d->scrollY) { +// d->hoverItem = iInvalidPos; +// updateVisible_SidebarWidget_(d); +// invalidate_SidebarWidget_(d); +// } +//} void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { if (d->mode == mode) return; if (d->mode >= 0 && d->mode < max_SidebarMode) { - d->modeScroll[d->mode] = d->scrollY; /* saved for later */ + d->modeScroll[d->mode] = scrollPos_ListWidget(d->list); /* saved for later */ } 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.333f, 1.333f, 3.5f, 1.2f }; - d->itemHeight = heights[mode] * lineHeight_Text(uiContent_FontId); - invalidate_SidebarWidget_(d); + setItemHeight_ListWidget(d->list, heights[mode] * lineHeight_Text(uiContent_FontId)); /* Restore previous scroll position. */ - d->scrollY = d->modeScroll[mode]; + setScrollPos_ListWidget(d->list, d->modeScroll[mode]); } enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { @@ -343,18 +359,15 @@ void init_SidebarWidget(iSidebarWidget *d) { setId_Widget(w, "sidebar"); setBackgroundColor_Widget(w, none_ColorId); setFlags_Widget(w, - hidden_WidgetFlag | hover_WidgetFlag | arrangeHorizontal_WidgetFlag | - resizeWidthOfChildren_WidgetFlag | collapse_WidgetFlag, + hidden_WidgetFlag | arrangeVertical_WidgetFlag | + resizeChildren_WidgetFlag | collapse_WidgetFlag, iTrue); - d->scrollY = 0; iZap(d->modeScroll); d->mode = -1; d->width = 60 * 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); d->maxButtonLabelWidth = 0; + /* TODO: Add a parent h-div for the mode buttons. */ for (int i = 0; i < max_SidebarMode; i++) { d->modeButtons[i] = addChildFlags_Widget( w, @@ -365,8 +378,7 @@ void init_SidebarWidget(iSidebarWidget *d) { iMaxi(d->maxButtonLabelWidth, 3 * gap_UI + measure_Text(uiLabel_FontId, normalModeLabels_[i]).x); } - addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); - setThumb_ScrollWidget(d->scroll, 0, 0); + addChildFlags_Widget(w, iClob(d->list = new_ListWidget()), expand_WidgetFlag); setMode_SidebarWidget(d, documentOutline_SidebarMode); d->resizer = addChildFlags_Widget( w, @@ -378,55 +390,51 @@ void init_SidebarWidget(iSidebarWidget *d) { setBackgroundColor_Widget(d->resizer, none_ColorId); d->resizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); d->menu = NULL; - init_IntSet(&d->invalidItems); - d->visBuffer = NULL; - d->visBufferValid = iFalse; } void deinit_SidebarWidget(iSidebarWidget *d) { SDL_FreeCursor(d->resizeCursor); - clearItems_SidebarWidget_(d); - deinit_Array(&d->items); - SDL_DestroyTexture(d->visBuffer); + //clearItems_SidebarWidget_(d); +// deinit_Array(&d->items); } -static int visCount_SidebarWidget_(const iSidebarWidget *d) { - return iMin(height_Rect(bounds_Widget(constAs_Widget(d))) / d->itemHeight, - (int) size_Array(&d->items)); -} +//static int visCount_SidebarWidget_(const iSidebarWidget *d) { +// return iMin(height_Rect(bounds_Widget(constAs_Widget(d))) / d->itemHeight, +// (int) size_Array(&d->items)); +//} -static iRanges visRange_SidebarWidget_(const iSidebarWidget *d) { - iRanges vis = { d->scrollY / d->itemHeight, 0 }; - vis.end = iMin(size_Array(&d->items), vis.start + visCount_SidebarWidget_(d)); - return vis; -} +//static iRanges visRange_SidebarWidget_(const iSidebarWidget *d) { +// iRanges vis = { d->scrollY / d->itemHeight, 0 }; +// vis.end = iMin(size_Array(&d->items), vis.start + visCount_SidebarWidget_(d)); +// return vis; +//} -static size_t itemIndex_SidebarWidget_(const iSidebarWidget *d, iInt2 pos) { - const iRect bounds = contentBounds_SidebarWidget_(d); - pos.y -= top_Rect(bounds) - d->scrollY; - if (pos.y < 0) return iInvalidPos; - size_t index = pos.y / d->itemHeight; - if (index >= size_Array(&d->items)) return iInvalidPos; - return index; -} +//static size_t itemIndex_SidebarWidget_(const iSidebarWidget *d, iInt2 pos) { +// const iRect bounds = contentBounds_SidebarWidget_(d); +// pos.y -= top_Rect(bounds) - d->scrollY; +// if (pos.y < 0) return iInvalidPos; +// size_t index = pos.y / d->itemHeight; +// if (index >= size_Array(&d->items)) return iInvalidPos; +// return index; +//} -static const iSidebarItem *constHoverItem_SidebarWidget_(const iSidebarWidget *d) { - if (d->hoverItem < size_Array(&d->items)) { - return constAt_Array(&d->items, d->hoverItem); - } - return NULL; -} +//static const iSidebarItem *constHoverItem_SidebarWidget_(const iSidebarWidget *d) { +// if (d->hoverItem < size_Array(&d->items)) { +// return constAt_Array(&d->items, d->hoverItem); +// } +// return NULL; +//} -static iSidebarItem *hoverItem_SidebarWidget_(iSidebarWidget *d) { - if (d->hoverItem < size_Array(&d->items)) { - return at_Array(&d->items, d->hoverItem); - } - return NULL; -} +//static iSidebarItem *hoverItem_SidebarWidget_(iSidebarWidget *d) { +// if (d->hoverItem < size_Array(&d->items)) { +// return at_Array(&d->items, d->hoverItem); +// } +// return NULL; +//} static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { if (d->mode == identities_SidebarMode) { - const iSidebarItem *hoverItem = constHoverItem_SidebarWidget_(d); + const iSidebarItem *hoverItem = constHoverItem_ListWidget(d->list); if (hoverItem) { return identity_GmCerts(certs_App(), hoverItem->id); } @@ -438,28 +446,28 @@ static iGmIdentity *hoverIdentity_SidebarWidget_(const iSidebarWidget *d) { return iConstCast(iGmIdentity *, constHoverIdentity_SidebarWidget_(d)); } -static void setHoverItem_SidebarWidget_(iSidebarWidget *d, size_t index) { - if (index < size_Array(&d->items)) { - if (constValue_Array(&d->items, index, iSidebarItem).isSeparator) { - index = iInvalidPos; - } - } - if (d->hoverItem != index) { - insert_IntSet(&d->invalidItems, d->hoverItem); - insert_IntSet(&d->invalidItems, index); - d->hoverItem = index; - refresh_Widget(as_Widget(d)); - } -} +//static void setHoverItem_SidebarWidget_(iSidebarWidget *d, size_t index) { +// if (index < size_Array(&d->items)) { +// if (constValue_Array(&d->items, index, iSidebarItem).isSeparator) { +// index = iInvalidPos; +// } +// } +// if (d->hoverItem != index) { +// insert_IntSet(&d->invalidItems, d->hoverItem); +// insert_IntSet(&d->invalidItems, index); +// d->hoverItem = index; +// refresh_Widget(as_Widget(d)); +// } +//} -static void updateMouseHover_SidebarWidget_(iSidebarWidget *d) { - const iInt2 mouse = mouseCoord_Window(get_Window()); - setHoverItem_SidebarWidget_(d, itemIndex_SidebarWidget_(d, mouse)); -} +//static void updateMouseHover_SidebarWidget_(iSidebarWidget *d) { +// const iInt2 mouse = mouseCoord_Window(get_Window()); +// setHoverItem_SidebarWidget_(d, itemIndex_SidebarWidget_(d, mouse)); +//} -static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { +static void itemClicked_SidebarWidget_(iSidebarWidget *d, const iSidebarItem *item) { setFocus_Widget(NULL); - const iSidebarItem *item = constAt_Array(&d->items, index); +// const iSidebarItem *item = constAt_Array(&d->items, index); switch (d->mode) { case documentOutline_SidebarMode: { const iGmDocument *doc = document_DocumentWidget(document_App()); @@ -485,7 +493,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { signIn_GmCerts(certs_App(), ident, tabUrl); } updateItems_SidebarWidget_(d); - updateMouseHover_SidebarWidget_(d); + updateMouseHover_ListWidget(d->list); } break; } @@ -520,7 +528,7 @@ void setWidth_SidebarWidget(iSidebarWidget *d, int width) { checkModeButtonLayout_SidebarWidget_(d); if (!isRefreshPending_App()) { updateSize_DocumentWidget(document_App()); - invalidate_SidebarWidget_(d); + invalidate_ListWidget(d->list); } } @@ -531,7 +539,7 @@ iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *c const iString *title = text_InputWidget(findChild_Widget(editor, "bmed.title")); const iString *url = text_InputWidget(findChild_Widget(editor, "bmed.url")); const iString *tags = text_InputWidget(findChild_Widget(editor, "bmed.tags")); - const iSidebarItem *item = hoverItem_SidebarWidget_(d); + const iSidebarItem *item = hoverItem_ListWidget(d->list); iAssert(item); /* hover item cannot have been changed */ iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); set_String(&bm->title, title); @@ -550,9 +558,9 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) iWidget *w = as_Widget(d); /* Handle commands. */ if (isResize_UserEvent(ev)) { - updateVisible_SidebarWidget_(d); + updateVisible_ListWidget(d->list); checkModeButtonLayout_SidebarWidget_(d); - invalidate_SidebarWidget_(d); + invalidate_ListWidget(d->list); } else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { const char *cmd = command_UserEvent(ev); @@ -580,6 +588,10 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) } return iTrue; } + else if (isCommand_Widget(w, ev, "list.clicked")) { + itemClicked_SidebarWidget_(d, pointerLabel_Command(cmd, "item")); + return iTrue; + } else if (equal_Command(cmd, "sidebar.width")) { setWidth_SidebarWidget(d, arg_Command(cmd)); return iTrue; @@ -590,7 +602,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) if (argLabel_Command(cmd, "show") && !isVisible_Widget(w)) { postCommand_App("sidebar.toggle arg:1"); } - scroll_SidebarWidget_(d, 0); + scrollOffset_ListWidget(d->list, 0); return iTrue; } else if (equal_Command(cmd, "sidebar.toggle")) { @@ -600,34 +612,25 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); if (isVisible_Widget(w)) { w->rect.size.x = d->width; - invalidate_SidebarWidget_(d); + invalidate_ListWidget(d->list); } arrange_Widget(w->parent); updateSize_DocumentWidget(document_App()); refresh_Widget(w->parent); return iTrue; } - else if (equal_Command(cmd, "scroll.moved")) { - d->scrollY = arg_Command(command_UserEvent(ev)); - d->hoverItem = iInvalidPos; - invalidate_SidebarWidget_(d); - return iTrue; - } else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { updateItems_SidebarWidget_(d); } - else if (equal_Command(cmd, "theme.changed")) { - invalidate_SidebarWidget_(d); - } else if (equal_Command(cmd, "bookmark.copy")) { - const iSidebarItem *item = hoverItem_SidebarWidget_(d); + const iSidebarItem *item = hoverItem_ListWidget(d->list); if (d->mode == bookmarks_SidebarMode && item) { SDL_SetClipboardText(cstr_String(&item->url)); } return iTrue; } else if (equal_Command(cmd, "bookmark.edit")) { - const iSidebarItem *item = hoverItem_SidebarWidget_(d); + const iSidebarItem *item = hoverItem_ListWidget(d->list); if (d->mode == bookmarks_SidebarMode && item) { setFlags_Widget(w, disabled_WidgetFlag, iTrue); iWidget *dlg = makeBookmarkEditor_Widget(); @@ -641,7 +644,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) return iTrue; } else if (equal_Command(cmd, "bookmark.delete")) { - const iSidebarItem *item = hoverItem_SidebarWidget_(d); + const iSidebarItem *item = hoverItem_ListWidget(d->list); if (d->mode == bookmarks_SidebarMode && item && remove_Bookmarks(bookmarks_App(), item->id)) { postCommand_App("bookmarks.changed"); } @@ -712,7 +715,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) return iTrue; } else if (equal_Command(cmd, "ident.delete")) { - iSidebarItem *item = hoverItem_SidebarWidget_(d); + iSidebarItem *item = hoverItem_ListWidget(d->list); if (argLabel_Command(cmd, "confirm")) { makeQuestion_Widget(uiTextCaution_ColorEscape "DELETE IDENTITY", format_CStr("Do you really want to delete the identity\n" @@ -732,23 +735,23 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) return iTrue; } else if (equal_Command(cmd, "history.delete")) { - const iSidebarItem *item = hoverItem_SidebarWidget_(d); + const iSidebarItem *item = hoverItem_ListWidget(d->list); if (item && !isEmpty_String(&item->url)) { removeUrl_Visited(visited_App(), &item->url); updateItems_SidebarWidget_(d); - scroll_SidebarWidget_(d, 0); + scrollOffset_ListWidget(d->list, 0); } return iTrue; } else if (equal_Command(cmd, "history.copy")) { - const iSidebarItem *item = hoverItem_SidebarWidget_(d); + const iSidebarItem *item = hoverItem_ListWidget(d->list); if (item && !isEmpty_String(&item->url)) { SDL_SetClipboardText(cstr_String(&item->url)); } return iTrue; } else if (equal_Command(cmd, "history.addbookmark")) { - const iSidebarItem *item = hoverItem_SidebarWidget_(d); + const iSidebarItem *item = hoverItem_ListWidget(d->list); if (!isEmpty_String(&item->url)) { makeBookmarkCreation_Widget( &item->url, @@ -768,53 +771,55 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) else { clear_Visited(visited_App()); updateItems_SidebarWidget_(d); - scroll_SidebarWidget_(d, 0); + scrollOffset_ListWidget(d->list, 0); } return iTrue; } } if (ev->type == SDL_MOUSEMOTION && !isVisible_Widget(d->menu)) { const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); - size_t hover = iInvalidPos; +// size_t hover = iInvalidPos; if (contains_Widget(d->resizer, mouse)) { setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_SIZEWE); } - else if (contains_Widget(constAs_Widget(d->scroll), mouse)) { - setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); - } - else if (contains_Widget(w, mouse)) { - hover = itemIndex_SidebarWidget_(d, mouse); - } - setHoverItem_SidebarWidget_(d, hover); +// else if (contains_Widget(constAs_Widget(d->scroll), mouse)) { +// setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); +// } +// else if (contains_Widget(w, mouse)) { +// hover = itemIndex_SidebarWidget_(d, mouse); +// } +// setHoverItem_SidebarWidget_(d, hover); /* Update cursor. */ - if (contains_Widget(w, mouse) && !contains_Widget(d->resizer, mouse) && - !contains_Widget(constAs_Widget(d->scroll), mouse)) { - const iSidebarItem *item = constHoverItem_SidebarWidget_(d); + else if (contains_Widget(w, mouse)) /* && !contains_Widget(d->resizer, mouse) && + !contains_Widget(constAs_Widget(d->scroll), mouse))*/ { + const iSidebarItem *item = constHoverItem_ListWidget(d->list); if (item && d->mode != identities_SidebarMode) { - setCursor_Window(get_Window(), item->isSeparator ? SDL_SYSTEM_CURSOR_ARROW - : SDL_SYSTEM_CURSOR_HAND); + setCursor_Window(get_Window(), + item->listItem.isSeparator ? SDL_SYSTEM_CURSOR_ARROW + : SDL_SYSTEM_CURSOR_HAND); } else { setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); } } } - if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { -#if defined (iPlatformApple) - /* Momentum scrolling. */ - scroll_SidebarWidget_(d, -ev->wheel.y * get_Window()->pixelRatio); -#else - scroll_SidebarWidget_(d, -ev->wheel.y * 3 * d->itemHeight); -#endif - return iTrue; - } +// if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { +//#if defined (iPlatformApple) +// /* Momentum scrolling. */ +// scroll_SidebarWidget_(d, -ev->wheel.y * get_Window()->pixelRatio); +//#else +// scroll_SidebarWidget_(d, -ev->wheel.y * 3 * d->itemHeight); +//#endif +// return iTrue; +// } if (d->menu && ev->type == SDL_MOUSEBUTTONDOWN) { if (ev->button.button == SDL_BUTTON_RIGHT) { if (!isVisible_Widget(d->menu)) { - setHoverItem_SidebarWidget_( - d, itemIndex_SidebarWidget_(d, init_I2(ev->button.x, ev->button.y))); + updateMouseHover_ListWidget(d->list); +// setHoverItem_ListWidget( +// d->list, itemIndex_ListWidget(d->list, init_I2(ev->button.x, ev->button.y))); } - if (d->hoverItem != iInvalidPos || isVisible_Widget(d->menu)) { + if (constHoverItem_ListWidget(d->list) || isVisible_Widget(d->menu)) { /* Update menu items. */ if (d->mode == identities_SidebarMode) { const iGmIdentity *ident = constHoverIdentity_SidebarWidget_(d); @@ -845,41 +850,27 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) } } processContextMenuEvent_Widget(d->menu, ev, {}); - switch (processEvent_Click(&d->click, ev)) { - case started_ClickResult: - //invalidate_SidebarWidget_(d); - break; - case finished_ClickResult: - if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && - d->hoverItem != iInvalidSize) { - itemClicked_SidebarWidget_(d, d->hoverItem); - } -// invalidate_SidebarWidget_(d); - break; - default: - break; - } +// switch (processEvent_Click(&d->click, ev)) { +// case started_ClickResult: +// //invalidate_SidebarWidget_(d); +// break; +// case finished_ClickResult: +// if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && +// d->hoverItem != iInvalidSize) { +// itemClicked_SidebarWidget_(d, d->hoverItem); +// } +// // invalidate_SidebarWidget_(d); +// break; +// default: +// break; +// } return processEvent_Widget(w, ev); } -static void allocVisBuffer_SidebarWidget_(iSidebarWidget *d) { - const iInt2 size = contentBounds_SidebarWidget_(d).size; - if (!d->visBuffer || !isEqual_I2(size_SDLTexture(d->visBuffer), size)) { - if (d->visBuffer) { - SDL_DestroyTexture(d->visBuffer); - } - d->visBuffer = SDL_CreateTexture(renderer_Window(get_Window()), - SDL_PIXELFORMAT_RGBA8888, - SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, - size.x, - size.y); - SDL_SetTextureBlendMode(d->visBuffer, SDL_BLENDMODE_NONE); - d->visBufferValid = iFalse; - } -} - static void draw_SidebarWidget_(const iSidebarWidget *d) { - const iWidget *w = constAs_Widget(d); + const iWidget *w = constAs_Widget(d); + const iRect bounds = bounds_Widget(w); +#if 0 const iRect bounds = contentBounds_SidebarWidget_(d); const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); iPaint p; @@ -1033,18 +1024,124 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { } endTarget_Paint(&p); /* Update state. */ { - iSidebarWidget *m = iConstCast(iSidebarWidget *, d); - m->visBufferValid = iTrue; - clear_IntSet(&m->invalidItems); +// iSidebarWidget *m = iConstCast(iSidebarWidget *, d); +// m->visBufferValid = iTrue; +// clear_IntSet(&m->invalidItems); } } SDL_RenderCopy( renderer_Window(get_Window()), d->visBuffer, NULL, (const SDL_Rect *) &bounds); +#endif + iPaint p; + init_Paint(&p); draw_Widget(w); - drawVLine_Paint(&p, - addX_I2(topRight_Rect(bounds_Widget(w)), -1), - height_Rect(bounds_Widget(w)), - uiSeparator_ColorId); + drawVLine_Paint( + &p, addX_I2(topRight_Rect(bounds), -1), height_Rect(bounds), uiSeparator_ColorId); +} + +static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, + const iListWidget *list) { + const iSidebarWidget *sidebar = (const iSidebarWidget *) constAs_Widget(list)->parent; + const iBool isPressing = isMouseDown_ListWidget(list); + const int font = uiContent_FontId; + const iBool isHover = + isHover_Widget(constAs_Widget(list)) && constHoverItem_ListWidget(list) == d; + const int itemHeight = height_Rect(itemRect); + const int iconColor = + isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) : uiIcon_ColorId; + if (isHover && !d->listItem.isSeparator) { + fillRect_Paint(p, + itemRect, + isPressing ? uiBackgroundPressed_ColorId + : uiBackgroundFramelessHover_ColorId); + } + iInt2 pos = itemRect.pos; + if (sidebar->mode == documentOutline_SidebarMode) { + const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) + : (tmHeading1_ColorId + d->indent / (4 * gap_UI)); + drawRange_Text(font, + init_I2(pos.x + 3 * gap_UI + d->indent, + mid_Rect(itemRect).y - lineHeight_Text(font) / 2), + fg, + range_String(&d->label)); + } + else if (sidebar->mode == bookmarks_SidebarMode) { + const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) + : uiText_ColorId; + iString str; + init_String(&str); + appendChar_String(&str, d->icon ? d->icon : 0x1f588); + const iRect iconArea = { addX_I2(pos, gap_UI), init_I2(7 * gap_UI, itemHeight) }; + drawCentered_Text(font, iconArea, iTrue, iconColor, "%s", cstr_String(&str)); + deinit_String(&str); + iInt2 textPos = addY_I2(topRight_Rect(iconArea), (itemHeight - lineHeight_Text(font)) / 2); + drawRange_Text(font, textPos, fg, range_String(&d->label)); + } + else if (sidebar->mode == history_SidebarMode) { + iBeginCollect(); + const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) + : uiText_ColorId; + if (d->listItem.isSeparator) { + if (!isEmpty_String(&d->meta)) { + unsetClip_Paint(p); + iInt2 drawPos = addY_I2(topLeft_Rect(itemRect), itemHeight * 0.666f); + drawHLine_Paint(p, drawPos, width_Rect(itemRect), uiIcon_ColorId); + drawRange_Text( + default_FontId, + add_I2(drawPos, + init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(default_FontId)) / 2)), + uiIcon_ColorId, + range_String(&d->meta)); + } + } + else { + iUrl parts; + init_Url(&parts, &d->url); + const iBool isGemini = equalCase_Rangecc(parts.scheme, "gemini"); + draw_Text(font, + add_I2(topLeft_Rect(itemRect), + init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)), + fg, + "%s%s%s%s%s%s", + isGemini ? "" : cstr_Rangecc(parts.scheme), + isGemini ? "" : "://", + escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId + : uiTextFramelessHover_ColorId) + : uiTextStrong_ColorId), + cstr_Rangecc(parts.host), + escape_Color(fg), + cstr_Rangecc(parts.path)); + } + iEndCollect(); + } + else if (sidebar->mode == identities_SidebarMode) { + const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) + : uiTextStrong_ColorId; + if (d->listItem.isSelected) { + drawRectThickness_Paint(p, + adjusted_Rect(itemRect, zero_I2(), init_I2(-2, -1)), + gap_UI / 4, + isHover && isPressing ? uiTextPressed_ColorId : uiIcon_ColorId); + } + iString icon; + initUnicodeN_String(&icon, &d->icon, 1); + iInt2 cPos = topLeft_Rect(itemRect); + addv_I2(&cPos, + init_I2(3 * gap_UI, + (itemHeight - lineHeight_Text(default_FontId) * 2 - lineHeight_Text(font)) / + 2)); + const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId + : uiTextFramelessHover_ColorId) + : uiText_ColorId; + drawRange_Text( + font, cPos, d->listItem.isSelected ? iconColor : metaFg, range_String(&icon)); + deinit_String(&icon); + drawRange_Text(font, add_I2(cPos, init_I2(6 * gap_UI, 0)), fg, range_String(&d->label)); + drawRange_Text(default_FontId, + add_I2(cPos, init_I2(6 * gap_UI, lineHeight_Text(font))), + metaFg, + range_String(&d->meta)); + } } iBeginDefineSubclass(SidebarWidget, Widget) diff --git a/src/ui/util.c b/src/ui/util.c index dfe364a5..120be3be 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -36,6 +36,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include +iBool isCommand_SDLEvent(const SDL_Event *d) { + return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode; +} + iBool isCommand_UserEvent(const SDL_Event *d, const char *cmd) { return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode && equal_Command(d->user.data1, cmd); diff --git a/src/ui/util.h b/src/ui/util.h index 9ef166de..ae3742b5 100644 --- a/src/ui/util.h +++ b/src/ui/util.h @@ -32,6 +32,7 @@ iDeclareType(Click) iDeclareType(Widget) iDeclareType(LabelWidget) +iBool isCommand_SDLEvent (const SDL_Event *d); iBool isCommand_UserEvent (const SDL_Event *, const char *cmd); const char * command_UserEvent (const SDL_Event *); diff --git a/src/ui/widget.c b/src/ui/widget.c index b5ea3b0f..cd02d5e4 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -207,6 +207,12 @@ iLocalDef iRect innerRect_Widget_(const iWidget *d) { height_Rect(d->rect) - d->padding[1] - d->padding[3]); } +iRect innerBounds_Widget(const iWidget *d) { + return adjusted_Rect(bounds_Widget(d), + init_I2(d->padding[0], d->padding[1]), + init_I2(-d->padding[2], -d->padding[3])); +} + void arrange_Widget(iWidget *d) { if (isCollapsed_Widget_(d)) { setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue); -- cgit v1.2.3