diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-09-05 10:11:10 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-09-05 10:11:10 +0300 |
commit | 8e2828efc6d9c88f39592f4ae1c709937b8b9f46 (patch) | |
tree | 81594e6be5ae4fe5f55ec6bcb4f1fe9c4c3c4603 | |
parent | aa50c1e0028c6cb08524dcdfb7188906c817e46f (diff) |
Refactor: ListWidget for a general purpose list
The sidebar uses a ListWidget now, but the layout needs fixing.
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/ui/listwidget.c | 323 | ||||
-rw-r--r-- | src/ui/listwidget.h | 69 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 607 | ||||
-rw-r--r-- | src/ui/util.c | 4 | ||||
-rw-r--r-- | src/ui/util.h | 1 | ||||
-rw-r--r-- | src/ui/widget.c | 6 |
7 files changed, 757 insertions, 255 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a4795d7..789e32f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -72,6 +72,8 @@ set (SOURCES | |||
72 | src/ui/command.h | 72 | src/ui/command.h |
73 | src/ui/documentwidget.c | 73 | src/ui/documentwidget.c |
74 | src/ui/documentwidget.h | 74 | src/ui/documentwidget.h |
75 | src/ui/listwidget.c | ||
76 | src/ui/listwidget.h | ||
75 | src/ui/metrics.c | 77 | src/ui/metrics.c |
76 | src/ui/metrics.h | 78 | src/ui/metrics.h |
77 | src/ui/paint.c | 79 | src/ui/paint.c |
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 @@ | |||
1 | /* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #include "listwidget.h" | ||
24 | #include "scrollwidget.h" | ||
25 | #include "paint.h" | ||
26 | #include "util.h" | ||
27 | #include "command.h" | ||
28 | |||
29 | #include <the_Foundation/intset.h> | ||
30 | |||
31 | void init_ListItem(iListItem *d) { | ||
32 | d->isSeparator = iFalse; | ||
33 | d->isSelected = iFalse; | ||
34 | } | ||
35 | |||
36 | void deinit_ListItem(iListItem *d) { | ||
37 | iUnused(d); | ||
38 | } | ||
39 | |||
40 | iDefineObjectConstruction(ListItem) | ||
41 | iDefineClass(ListItem) | ||
42 | |||
43 | /*----------------------------------------------------------------------------------------------*/ | ||
44 | |||
45 | iDefineObjectConstruction(ListWidget) | ||
46 | |||
47 | struct Impl_ListWidget { | ||
48 | iWidget widget; | ||
49 | iScrollWidget *scroll; | ||
50 | int scrollY; | ||
51 | int itemHeight; | ||
52 | iPtrArray items; | ||
53 | size_t hoverItem; | ||
54 | iClick click; | ||
55 | iIntSet invalidItems; | ||
56 | SDL_Texture *visBuffer; | ||
57 | iBool visBufferValid; | ||
58 | }; | ||
59 | |||
60 | void init_ListWidget(iListWidget *d) { | ||
61 | iWidget *w = as_Widget(d); | ||
62 | init_Widget(w); | ||
63 | setId_Widget(w, "list"); | ||
64 | setFlags_Widget(w, hover_WidgetFlag, iTrue); | ||
65 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | ||
66 | setThumb_ScrollWidget(d->scroll, 0, 0); | ||
67 | d->scrollY = 0; | ||
68 | init_PtrArray(&d->items); | ||
69 | d->hoverItem = iInvalidPos; | ||
70 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | ||
71 | init_IntSet(&d->invalidItems); | ||
72 | d->visBuffer = NULL; | ||
73 | d->visBufferValid = iFalse; | ||
74 | } | ||
75 | |||
76 | void deinit_ListWidget(iListWidget *d) { | ||
77 | clear_ListWidget(d); | ||
78 | deinit_PtrArray(&d->items); | ||
79 | SDL_DestroyTexture(d->visBuffer); | ||
80 | } | ||
81 | |||
82 | void invalidate_ListWidget(iListWidget *d) { | ||
83 | d->visBufferValid = iFalse; | ||
84 | clear_IntSet(&d->invalidItems); /* all will be drawn */ | ||
85 | refresh_Widget(as_Widget(d)); | ||
86 | } | ||
87 | |||
88 | void clear_ListWidget(iListWidget *d) { | ||
89 | iForEach(PtrArray, i, &d->items) { | ||
90 | deref_Object(i.ptr); | ||
91 | } | ||
92 | clear_PtrArray(&d->items); | ||
93 | d->hoverItem = iInvalidPos; | ||
94 | } | ||
95 | |||
96 | void addItem_ListWidget(iListWidget *d, iAnyObject *item) { | ||
97 | pushBack_PtrArray(&d->items, ref_Object(item)); | ||
98 | } | ||
99 | |||
100 | iScrollWidget *scroll_ListWidget(iListWidget *d) { | ||
101 | return d->scroll; | ||
102 | } | ||
103 | |||
104 | size_t numItems_ListWidget(const iListWidget *d) { | ||
105 | return size_PtrArray(&d->items); | ||
106 | } | ||
107 | |||
108 | static int scrollMax_ListWidget_(const iListWidget *d) { | ||
109 | return iMax(0, | ||
110 | (int) size_PtrArray(&d->items) * d->itemHeight - | ||
111 | height_Rect(innerBounds_Widget(constAs_Widget(d)))); | ||
112 | } | ||
113 | |||
114 | void updateVisible_ListWidget(iListWidget *d) { | ||
115 | const int contentSize = size_PtrArray(&d->items) * d->itemHeight; | ||
116 | const iRect bounds = innerBounds_Widget(as_Widget(d)); | ||
117 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_ListWidget_(d) }); | ||
118 | setThumb_ScrollWidget(d->scroll, | ||
119 | d->scrollY, | ||
120 | contentSize > 0 ? height_Rect(bounds_Widget(as_Widget(d->scroll))) * | ||
121 | height_Rect(bounds) / contentSize | ||
122 | : 0); | ||
123 | } | ||
124 | |||
125 | void setItemHeight_ListWidget(iListWidget *d, int itemHeight) { | ||
126 | d->itemHeight = itemHeight; | ||
127 | invalidate_ListWidget(d); | ||
128 | } | ||
129 | |||
130 | int scrollPos_ListWidget(const iListWidget *d) { | ||
131 | return d->scrollY; | ||
132 | } | ||
133 | |||
134 | void setScrollPos_ListWidget(iListWidget *d, int pos) { | ||
135 | d->scrollY = pos; | ||
136 | d->hoverItem = iInvalidPos; | ||
137 | invalidate_ListWidget(d); | ||
138 | } | ||
139 | |||
140 | void scrollOffset_ListWidget(iListWidget *d, int offset) { | ||
141 | const int oldScroll = d->scrollY; | ||
142 | d->scrollY += offset; | ||
143 | if (d->scrollY < 0) { | ||
144 | d->scrollY = 0; | ||
145 | } | ||
146 | const int scrollMax = scrollMax_ListWidget_(d); | ||
147 | d->scrollY = iMin(d->scrollY, scrollMax); | ||
148 | if (oldScroll != d->scrollY) { | ||
149 | d->hoverItem = iInvalidPos; | ||
150 | updateVisible_ListWidget(d); | ||
151 | invalidate_ListWidget(d); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | static int visCount_ListWidget_(const iListWidget *d) { | ||
156 | return iMin(height_Rect(innerBounds_Widget(constAs_Widget(d))) / d->itemHeight, | ||
157 | (int) size_PtrArray(&d->items)); | ||
158 | } | ||
159 | |||
160 | static iRanges visRange_ListWidget_(const iListWidget *d) { | ||
161 | iRanges vis = { d->scrollY / d->itemHeight, 0 }; | ||
162 | vis.end = iMin(size_PtrArray(&d->items), vis.start + visCount_ListWidget_(d)); | ||
163 | return vis; | ||
164 | } | ||
165 | |||
166 | size_t itemIndex_ListWidget(const iListWidget *d, iInt2 pos) { | ||
167 | const iRect bounds = innerBounds_Widget(constAs_Widget(d)); | ||
168 | pos.y -= top_Rect(bounds) - d->scrollY; | ||
169 | if (pos.y < 0) return iInvalidPos; | ||
170 | size_t index = pos.y / d->itemHeight; | ||
171 | if (index >= size_Array(&d->items)) return iInvalidPos; | ||
172 | return index; | ||
173 | } | ||
174 | |||
175 | const iAnyObject *constHoverItem_ListWidget(const iListWidget *d) { | ||
176 | if (d->hoverItem < size_PtrArray(&d->items)) { | ||
177 | return constAt_PtrArray(&d->items, d->hoverItem); | ||
178 | } | ||
179 | return NULL; | ||
180 | } | ||
181 | |||
182 | iAnyObject *hoverItem_ListWidget(iListWidget *d) { | ||
183 | if (d->hoverItem < size_PtrArray(&d->items)) { | ||
184 | return at_PtrArray(&d->items, d->hoverItem); | ||
185 | } | ||
186 | return NULL; | ||
187 | } | ||
188 | |||
189 | static void setHoverItem_ListWidget_(iListWidget *d, size_t index) { | ||
190 | if (index < size_PtrArray(&d->items)) { | ||
191 | const iListItem *item = at_PtrArray(&d->items, index); | ||
192 | if (item->isSeparator) { | ||
193 | index = iInvalidPos; | ||
194 | } | ||
195 | } | ||
196 | if (d->hoverItem != index) { | ||
197 | insert_IntSet(&d->invalidItems, d->hoverItem); | ||
198 | insert_IntSet(&d->invalidItems, index); | ||
199 | d->hoverItem = index; | ||
200 | refresh_Widget(as_Widget(d)); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | void updateMouseHover_ListWidget(iListWidget *d) { | ||
205 | const iInt2 mouse = mouseCoord_Window(get_Window()); | ||
206 | setHoverItem_ListWidget_(d, itemIndex_ListWidget(d, mouse)); | ||
207 | } | ||
208 | |||
209 | static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | ||
210 | iWidget *w = as_Widget(d); | ||
211 | if (isCommand_SDLEvent(ev)) { | ||
212 | const char *cmd = command_UserEvent(ev); | ||
213 | if (equal_Command(cmd, "theme.changed")) { | ||
214 | invalidate_ListWidget(d); | ||
215 | } | ||
216 | else if (isCommand_Widget(w, ev, "scroll.moved")) { | ||
217 | setScrollPos_ListWidget(d, arg_Command(cmd)); | ||
218 | return iTrue; | ||
219 | } | ||
220 | } | ||
221 | if (ev->type == SDL_MOUSEMOTION) { | ||
222 | const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); | ||
223 | size_t hover = iInvalidPos; | ||
224 | if (!contains_Widget(constAs_Widget(d->scroll), mouse) && | ||
225 | contains_Widget(w, mouse)) { | ||
226 | hover = itemIndex_ListWidget(d, mouse); | ||
227 | } | ||
228 | setHoverItem_ListWidget_(d, hover); | ||
229 | } | ||
230 | if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { | ||
231 | #if defined (iPlatformApple) | ||
232 | /* Momentum scrolling. */ | ||
233 | scrollOffset_ListWidget(d, -ev->wheel.y * get_Window()->pixelRatio); | ||
234 | #else | ||
235 | scrollOffset_ListWidget(d, -ev->wheel.y * 3 * d->itemHeight); | ||
236 | #endif | ||
237 | return iTrue; | ||
238 | } | ||
239 | switch (processEvent_Click(&d->click, ev)) { | ||
240 | case started_ClickResult: | ||
241 | //invalidate_SidebarWidget_(d); | ||
242 | break; | ||
243 | case finished_ClickResult: | ||
244 | if (contains_Rect(innerBounds_Widget(w), pos_Click(&d->click)) && | ||
245 | d->hoverItem != iInvalidSize) { | ||
246 | //itemClicked_SidebarWidget_(d, d->hoverItem); | ||
247 | postCommand_Widget(w, "list.clicked arg:%zu item:%p", | ||
248 | d->hoverItem, constHoverItem_ListWidget(d)); | ||
249 | } | ||
250 | // invalidate_SidebarWidget_(d); | ||
251 | break; | ||
252 | default: | ||
253 | break; | ||
254 | } | ||
255 | return processEvent_Widget(w, ev); | ||
256 | } | ||
257 | |||
258 | static void allocVisBuffer_ListWidget_(iListWidget *d) { | ||
259 | const iInt2 size = innerBounds_Widget(as_Widget(d)).size; | ||
260 | if (!d->visBuffer || !isEqual_I2(size_SDLTexture(d->visBuffer), size)) { | ||
261 | if (d->visBuffer) { | ||
262 | SDL_DestroyTexture(d->visBuffer); | ||
263 | } | ||
264 | d->visBuffer = SDL_CreateTexture(renderer_Window(get_Window()), | ||
265 | SDL_PIXELFORMAT_RGBA8888, | ||
266 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
267 | size.x, | ||
268 | size.y); | ||
269 | SDL_SetTextureBlendMode(d->visBuffer, SDL_BLENDMODE_NONE); | ||
270 | d->visBufferValid = iFalse; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | static void draw_ListWidget_(const iListWidget *d) { | ||
275 | const iWidget *w = constAs_Widget(d); | ||
276 | const iRect bounds = innerBounds_Widget(w); | ||
277 | draw_Widget(w); /* background */ | ||
278 | iPaint p; | ||
279 | init_Paint(&p); | ||
280 | if (!d->visBufferValid || !isEmpty_IntSet(&d->invalidItems)) { | ||
281 | iListWidget *m = iConstCast(iListWidget *, d); | ||
282 | allocVisBuffer_ListWidget_(m); | ||
283 | beginTarget_Paint(&p, d->visBuffer); | ||
284 | const iRect bufBounds = (iRect){ zero_I2(), bounds.size }; | ||
285 | if (!d->visBufferValid) { | ||
286 | fillRect_Paint(&p, bufBounds, w->bgColor); | ||
287 | } | ||
288 | /* Draw items. */ { | ||
289 | const iRanges visRange = visRange_ListWidget_(d); | ||
290 | iInt2 pos = init_I2(0, -(d->scrollY % d->itemHeight)); | ||
291 | for (size_t i = visRange.start; i < visRange.end; i++) { | ||
292 | /* TODO: Refactor to loop through invalidItems only. */ | ||
293 | if (!d->visBufferValid || contains_IntSet(&d->invalidItems, i)) { | ||
294 | const iListItem *item = constAt_PtrArray(&d->items, i); | ||
295 | const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; | ||
296 | setClip_Paint(&p, intersect_Rect(itemRect, bufBounds)); | ||
297 | if (d->visBufferValid) { | ||
298 | fillRect_Paint(&p, itemRect, w->bgColor); | ||
299 | } | ||
300 | class_ListItem(item)->draw(item, &p, itemRect, d); | ||
301 | unsetClip_Paint(&p); | ||
302 | } | ||
303 | pos.y += d->itemHeight; | ||
304 | } | ||
305 | } | ||
306 | endTarget_Paint(&p); | ||
307 | /* Update state. */ | ||
308 | m->visBufferValid = iTrue; | ||
309 | clear_IntSet(&m->invalidItems); | ||
310 | } | ||
311 | SDL_RenderCopy( | ||
312 | renderer_Window(get_Window()), d->visBuffer, NULL, (const SDL_Rect *) &bounds); | ||
313 | } | ||
314 | |||
315 | iBool isMouseDown_ListWidget(const iListWidget *d) { | ||
316 | return d->click.isActive && | ||
317 | contains_Rect(innerBounds_Widget(constAs_Widget(d)), pos_Click(&d->click)); | ||
318 | } | ||
319 | |||
320 | iBeginDefineSubclass(ListWidget, Widget) | ||
321 | .processEvent = (iAny *) processEvent_ListWidget_, | ||
322 | .draw = (iAny *) draw_ListWidget_, | ||
323 | 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 @@ | |||
1 | /* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #pragma once | ||
24 | |||
25 | #include "scrollwidget.h" | ||
26 | #include "paint.h" | ||
27 | |||
28 | #include <the_Foundation/ptrarray.h> | ||
29 | |||
30 | iDeclareType(ListWidget) | ||
31 | |||
32 | iBeginDeclareClass(ListItem) | ||
33 | void (*draw) (const iAnyObject *, iPaint *p, iRect rect, const iListWidget *list); | ||
34 | iEndDeclareClass(ListItem) | ||
35 | |||
36 | iDeclareType(ListItem) | ||
37 | |||
38 | struct Impl_ListItem { | ||
39 | iObject object; | ||
40 | iBool isSeparator; | ||
41 | iBool isSelected; | ||
42 | }; | ||
43 | |||
44 | iDeclareObjectConstruction(ListItem) | ||
45 | |||
46 | iDeclareWidgetClass(ListWidget) | ||
47 | iDeclareObjectConstruction(ListWidget) | ||
48 | |||
49 | void setItemHeight_ListWidget(iListWidget *, int itemHeight); | ||
50 | |||
51 | void invalidate_ListWidget (iListWidget *); | ||
52 | void clear_ListWidget (iListWidget *); | ||
53 | void addItem_ListWidget (iListWidget *, iAnyObject *item); | ||
54 | |||
55 | iScrollWidget * scroll_ListWidget (iListWidget *); | ||
56 | |||
57 | int scrollPos_ListWidget (const iListWidget *); | ||
58 | |||
59 | void setScrollPos_ListWidget (iListWidget *, int pos); | ||
60 | void scrollOffset_ListWidget (iListWidget *, int offset); | ||
61 | void updateVisible_ListWidget (iListWidget *); | ||
62 | void updateMouseHover_ListWidget (iListWidget *); | ||
63 | |||
64 | size_t numItems_ListWidget (const iListWidget *); | ||
65 | size_t itemIndex_ListWidget(const iListWidget *, iInt2 pos); | ||
66 | const iAnyObject *constHoverItem_ListWidget(const iListWidget *); | ||
67 | iAnyObject *hoverItem_ListWidget(iListWidget *); | ||
68 | |||
69 | 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. */ | |||
30 | #include "gmdocument.h" | 30 | #include "gmdocument.h" |
31 | #include "inputwidget.h" | 31 | #include "inputwidget.h" |
32 | #include "labelwidget.h" | 32 | #include "labelwidget.h" |
33 | #include "listwidget.h" | ||
33 | #include "paint.h" | 34 | #include "paint.h" |
34 | #include "scrollwidget.h" | 35 | #include "scrollwidget.h" |
35 | #include "util.h" | 36 | #include "util.h" |
@@ -41,27 +42,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
41 | #include <SDL_mouse.h> | 42 | #include <SDL_mouse.h> |
42 | 43 | ||
43 | iDeclareType(SidebarItem) | 44 | iDeclareType(SidebarItem) |
45 | typedef iListItemClass iSidebarItemClass; | ||
44 | 46 | ||
45 | struct Impl_SidebarItem { | 47 | struct Impl_SidebarItem { |
46 | uint32_t id; | 48 | iListItem listItem; |
47 | int indent; | 49 | uint32_t id; |
48 | iChar icon; | 50 | int indent; |
49 | iString label; | 51 | iChar icon; |
50 | iString meta; | 52 | iString label; |
51 | iString url; | 53 | iString meta; |
52 | iBool isSeparator; | 54 | iString url; |
53 | iBool isSelected; | ||
54 | }; | 55 | }; |
55 | 56 | ||
56 | void init_SidebarItem(iSidebarItem *d) { | 57 | void init_SidebarItem(iSidebarItem *d) { |
58 | init_ListItem(&d->listItem); | ||
57 | d->id = 0; | 59 | d->id = 0; |
58 | d->indent = 0; | 60 | d->indent = 0; |
59 | d->icon = 0; | 61 | d->icon = 0; |
60 | init_String(&d->label); | 62 | init_String(&d->label); |
61 | init_String(&d->meta); | 63 | init_String(&d->meta); |
62 | init_String(&d->url); | 64 | init_String(&d->url); |
63 | d->isSeparator = iFalse; | ||
64 | d->isSelected = iFalse; | ||
65 | } | 65 | } |
66 | 66 | ||
67 | void deinit_SidebarItem(iSidebarItem *d) { | 67 | void deinit_SidebarItem(iSidebarItem *d) { |
@@ -70,53 +70,60 @@ void deinit_SidebarItem(iSidebarItem *d) { | |||
70 | deinit_String(&d->label); | 70 | deinit_String(&d->label); |
71 | } | 71 | } |
72 | 72 | ||
73 | iDefineTypeConstruction(SidebarItem) | 73 | static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, const iListWidget *list); |
74 | |||
75 | iBeginDefineSubclass(SidebarItem, ListItem) | ||
76 | .draw = (iAny *) draw_SidebarItem_, | ||
77 | iEndDefineSubclass(SidebarItem) | ||
78 | |||
79 | iDefineObjectConstruction(SidebarItem) | ||
74 | 80 | ||
75 | /*----------------------------------------------------------------------------------------------*/ | 81 | /*----------------------------------------------------------------------------------------------*/ |
76 | 82 | ||
77 | struct Impl_SidebarWidget { | 83 | struct Impl_SidebarWidget { |
78 | iWidget widget; | 84 | iWidget widget; |
79 | enum iSidebarMode mode; | 85 | enum iSidebarMode mode; |
80 | iScrollWidget *scroll; | 86 | iListWidget *list; |
81 | int scrollY; | 87 | // iScrollWidget *scroll; |
88 | // int scrollY; | ||
82 | int modeScroll[max_SidebarMode]; | 89 | int modeScroll[max_SidebarMode]; |
83 | int width; | 90 | int width; |
84 | iLabelWidget *modeButtons[max_SidebarMode]; | 91 | iLabelWidget *modeButtons[max_SidebarMode]; |
85 | int itemHeight; | 92 | int itemHeight; |
86 | int maxButtonLabelWidth; | 93 | int maxButtonLabelWidth; |
87 | iArray items; | 94 | // iArray items; |
88 | size_t hoverItem; | 95 | // size_t hoverItem; |
89 | iClick click; | 96 | // iClick click; |
90 | iWidget *resizer; | 97 | iWidget *resizer; |
91 | SDL_Cursor *resizeCursor; | 98 | SDL_Cursor *resizeCursor; |
92 | iWidget *menu; | 99 | iWidget *menu; |
93 | iIntSet invalidItems; | 100 | // iIntSet invalidItems; |
94 | SDL_Texture *visBuffer; | 101 | // SDL_Texture *visBuffer; |
95 | iBool visBufferValid; | 102 | // iBool visBufferValid; |
96 | }; | 103 | }; |
97 | 104 | ||
98 | iDefineObjectConstruction(SidebarWidget) | 105 | iDefineObjectConstruction(SidebarWidget) |
99 | 106 | ||
100 | static void invalidate_SidebarWidget_(iSidebarWidget *d) { | 107 | //static void invalidate_SidebarWidget_(iSidebarWidget *d) { |
101 | d->visBufferValid = iFalse; | 108 | // d->visBufferValid = iFalse; |
102 | refresh_Widget(as_Widget(d)); | 109 | // refresh_Widget(as_Widget(d)); |
103 | clear_IntSet(&d->invalidItems); /* all will be drawn */ | 110 | // clear_IntSet(&d->invalidItems); /* all will be drawn */ |
104 | } | 111 | //} |
105 | 112 | ||
106 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { | 113 | static iBool isResizing_SidebarWidget_(const iSidebarWidget *d) { |
107 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; | 114 | return (flags_Widget(d->resizer) & pressed_WidgetFlag) != 0; |
108 | } | 115 | } |
109 | 116 | ||
110 | static void clearItems_SidebarWidget_(iSidebarWidget *d) { | 117 | //static void clearItems_SidebarWidget_(iSidebarWidget *d) { |
111 | iForEach(Array, i, &d->items) { | 118 | // iForEach(Array, i, &d->items) { |
112 | deinit_SidebarItem(i.value); | 119 | // deinit_SidebarItem(i.value); |
113 | } | 120 | // } |
114 | clear_Array(&d->items); | 121 | // clear_Array(&d->items); |
115 | } | 122 | //} |
116 | 123 | ||
117 | static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { | 124 | static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { |
118 | iRect bounds = bounds_Widget(constAs_Widget(d)); | 125 | iRect bounds = bounds_Widget(constAs_Widget(d)); |
119 | const iWidget *scroll = constAs_Widget(d->scroll); | 126 | const iWidget *scroll = constAs_Widget(scroll_ListWidget(d->list)); |
120 | adjustEdges_Rect(&bounds, | 127 | adjustEdges_Rect(&bounds, |
121 | as_Widget(d->modeButtons[0])->rect.size.y + gap_UI, | 128 | as_Widget(d->modeButtons[0])->rect.size.y + gap_UI, |
122 | isVisible_Widget(scroll) ? -scroll->rect.size.x : 0, | 129 | isVisible_Widget(scroll) ? -scroll->rect.size.x : 0, |
@@ -125,57 +132,60 @@ static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { | |||
125 | return bounds; | 132 | return bounds; |
126 | } | 133 | } |
127 | 134 | ||
128 | static int scrollMax_SidebarWidget_(const iSidebarWidget *d) { | 135 | //static int scrollMax_SidebarWidget_(const iSidebarWidget *d) { |
129 | return iMax(0, | 136 | // return iMax(0, |
130 | (int) size_Array(&d->items) * d->itemHeight - | 137 | // (int) numItems_ListWidget(d->list) * d->itemHeight - |
131 | height_Rect(contentBounds_SidebarWidget_(d))); | 138 | // height_Rect(contentBounds_SidebarWidget_(d))); |
132 | } | 139 | //} |
133 | 140 | ||
134 | static void updateVisible_SidebarWidget_(iSidebarWidget *d) { | 141 | //static void updateVisible_SidebarWidget_(iSidebarWidget *d) { |
135 | const int contentSize = size_Array(&d->items) * d->itemHeight; | 142 | // const int contentSize = size_Array(&d->items) * d->itemHeight; |
136 | const iRect bounds = contentBounds_SidebarWidget_(d); | 143 | // const iRect bounds = contentBounds_SidebarWidget_(d); |
137 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_SidebarWidget_(d) }); | 144 | // setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_SidebarWidget_(d) }); |
138 | setThumb_ScrollWidget(d->scroll, | 145 | // setThumb_ScrollWidget(d->scroll, |
139 | d->scrollY, | 146 | // d->scrollY, |
140 | contentSize > 0 ? height_Rect(bounds_Widget(as_Widget(d->scroll))) * | 147 | // contentSize > 0 ? height_Rect(bounds_Widget(as_Widget(d->scroll))) * |
141 | height_Rect(bounds) / contentSize | 148 | // height_Rect(bounds) / contentSize |
142 | : 0); | 149 | // : 0); |
143 | } | 150 | //} |
144 | 151 | ||
145 | static int cmpTitle_Bookmark_(const iBookmark **a, const iBookmark **b) { | 152 | static int cmpTitle_Bookmark_(const iBookmark **a, const iBookmark **b) { |
146 | return cmpStringCase_String(&(*a)->title, &(*b)->title); | 153 | return cmpStringCase_String(&(*a)->title, &(*b)->title); |
147 | } | 154 | } |
148 | 155 | ||
149 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { | 156 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { |
150 | clearItems_SidebarWidget_(d); | 157 | // clearItems_SidebarWidget_(d); |
158 | clear_ListWidget(d->list); | ||
151 | destroy_Widget(d->menu); | 159 | destroy_Widget(d->menu); |
152 | d->menu = NULL; | 160 | d->menu = NULL; |
153 | d->hoverItem = iInvalidPos; | 161 | // d->hoverItem = iInvalidPos; |
154 | switch (d->mode) { | 162 | switch (d->mode) { |
155 | case documentOutline_SidebarMode: { | 163 | case documentOutline_SidebarMode: { |
156 | const iGmDocument *doc = document_DocumentWidget(document_App()); | 164 | const iGmDocument *doc = document_DocumentWidget(document_App()); |
157 | iConstForEach(Array, i, headings_GmDocument(doc)) { | 165 | iConstForEach(Array, i, headings_GmDocument(doc)) { |
158 | const iGmHeading *head = i.value; | 166 | const iGmHeading *head = i.value; |
159 | iSidebarItem item; | 167 | iSidebarItem *item = new_SidebarItem(); |
160 | init_SidebarItem(&item); | 168 | // init_SidebarItem(&item); |
161 | item.id = index_ArrayConstIterator(&i); | 169 | item->id = index_ArrayConstIterator(&i); |
162 | setRange_String(&item.label, head->text); | 170 | setRange_String(&item->label, head->text); |
163 | item.indent = head->level * 4 * gap_UI; | 171 | item->indent = head->level * 4 * gap_UI; |
164 | pushBack_Array(&d->items, &item); | 172 | addItem_ListWidget(d->list, item); |
173 | iRelease(item); | ||
165 | } | 174 | } |
166 | break; | 175 | break; |
167 | } | 176 | } |
168 | case bookmarks_SidebarMode: { | 177 | case bookmarks_SidebarMode: { |
169 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), NULL, cmpTitle_Bookmark_)) { | 178 | iConstForEach(PtrArray, i, list_Bookmarks(bookmarks_App(), NULL, cmpTitle_Bookmark_)) { |
170 | const iBookmark *bm = i.ptr; | 179 | const iBookmark *bm = i.ptr; |
171 | iSidebarItem item; | 180 | iSidebarItem *item = new_SidebarItem(); |
172 | init_SidebarItem(&item); | 181 | // init_SidebarItem(&item); |
173 | item.id = id_Bookmark(bm); | 182 | item->id = id_Bookmark(bm); |
174 | item.icon = bm->icon; | 183 | item->icon = bm->icon; |
175 | set_String(&item.url, &bm->url); | 184 | set_String(&item->url, &bm->url); |
176 | set_String(&item.label, &bm->title); | 185 | set_String(&item->label, &bm->title); |
177 | set_String(&item.meta, &bm->tags); | 186 | set_String(&item->meta, &bm->tags); |
178 | pushBack_Array(&d->items, &item); | 187 | addItem_ListWidget(d->list, item); |
188 | iRelease(item); | ||
179 | } | 189 | } |
180 | d->menu = makeMenu_Widget( | 190 | d->menu = makeMenu_Widget( |
181 | as_Widget(d), | 191 | as_Widget(d), |
@@ -192,27 +202,32 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
192 | const int thisYear = on.year; | 202 | const int thisYear = on.year; |
193 | iConstForEach(PtrArray, i, list_Visited(visited_App(), 200)) { | 203 | iConstForEach(PtrArray, i, list_Visited(visited_App(), 200)) { |
194 | const iVisitedUrl *visit = i.ptr; | 204 | const iVisitedUrl *visit = i.ptr; |
195 | iSidebarItem item; | 205 | iSidebarItem *item = new_SidebarItem(); |
196 | init_SidebarItem(&item); | 206 | // init_SidebarItem(&item); |
197 | set_String(&item.url, &visit->url); | 207 | set_String(&item->url, &visit->url); |
198 | iDate date; | 208 | iDate date; |
199 | init_Date(&date, &visit->when); | 209 | init_Date(&date, &visit->when); |
200 | if (date.day != on.day || date.month != on.month || date.year != on.year) { | 210 | if (date.day != on.day || date.month != on.month || date.year != on.year) { |
201 | on = date; | 211 | on = date; |
202 | /* Date separator. */ | 212 | /* Date separator. */ |
203 | iSidebarItem sep; | 213 | iSidebarItem *sep = new_SidebarItem(); |
204 | init_SidebarItem(&sep); | 214 | // init_SidebarItem(&sep); |
205 | sep.isSeparator = iTrue; | 215 | sep->listItem.isSeparator = iTrue; |
206 | set_String(&sep.meta, | 216 | set_String(&sep->meta, |
207 | collect_String(format_Date( | 217 | collect_String(format_Date( |
208 | &date, date.year != thisYear ? "%b %d %Y" : "%b %d"))); | 218 | &date, date.year != thisYear ? "%b %d %Y" : "%b %d"))); |
209 | pushBack_Array(&d->items, &sep); | 219 | // pushBack_Array(&d->items, &sep); |
220 | addItem_ListWidget(d->list, sep); | ||
221 | iRelease(sep); | ||
210 | /* Date separators are two items tall. */ | 222 | /* Date separators are two items tall. */ |
211 | init_SidebarItem(&sep); | 223 | sep = new_SidebarItem(); |
212 | sep.isSeparator = iTrue; | 224 | sep->listItem.isSeparator = iTrue; |
213 | pushBack_Array(&d->items, &sep); | 225 | addItem_ListWidget(d->list, sep); |
226 | iRelease(sep); | ||
214 | } | 227 | } |
215 | pushBack_Array(&d->items, &item); | 228 | // pushBack_Array(&d->items, &item); |
229 | addItem_ListWidget(d->list, item); | ||
230 | iRelease(item); | ||
216 | } | 231 | } |
217 | d->menu = makeMenu_Widget( | 232 | d->menu = makeMenu_Widget( |
218 | as_Widget(d), | 233 | as_Widget(d), |
@@ -230,16 +245,16 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
230 | const iString *tabUrl = url_DocumentWidget(document_App()); | 245 | const iString *tabUrl = url_DocumentWidget(document_App()); |
231 | iConstForEach(PtrArray, i, identities_GmCerts(certs_App())) { | 246 | iConstForEach(PtrArray, i, identities_GmCerts(certs_App())) { |
232 | const iGmIdentity *ident = i.ptr; | 247 | const iGmIdentity *ident = i.ptr; |
233 | iSidebarItem item; | 248 | iSidebarItem *item = new_SidebarItem(); |
234 | init_SidebarItem(&item); | 249 | // init_SidebarItem(&item); |
235 | item.id = index_PtrArrayConstIterator(&i); | 250 | item->id = index_PtrArrayConstIterator(&i); |
236 | item.icon = ident->icon; | 251 | item->icon = ident->icon; |
237 | set_String(&item.label, collect_String(subject_TlsCertificate(ident->cert))); | 252 | set_String(&item->label, collect_String(subject_TlsCertificate(ident->cert))); |
238 | iDate until; | 253 | iDate until; |
239 | validUntil_TlsCertificate(ident->cert, &until); | 254 | validUntil_TlsCertificate(ident->cert, &until); |
240 | const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl); | 255 | const iBool isActive = isUsedOn_GmIdentity(ident, tabUrl); |
241 | format_String( | 256 | format_String( |
242 | &item.meta, | 257 | &item->meta, |
243 | "%s", | 258 | "%s", |
244 | isActive ? "Using" | 259 | isActive ? "Using" |
245 | : isUsed_GmIdentity(ident) | 260 | : isUsed_GmIdentity(ident) |
@@ -250,17 +265,19 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
250 | ? "Temporary" | 265 | ? "Temporary" |
251 | : cstrCollect_String(format_Date(&until, "Expires %b %d, %Y")); | 266 | : cstrCollect_String(format_Date(&until, "Expires %b %d, %Y")); |
252 | if (isEmpty_String(&ident->notes)) { | 267 | if (isEmpty_String(&ident->notes)) { |
253 | appendFormat_String(&item.meta, "\n%s", expiry); | 268 | appendFormat_String(&item->meta, "\n%s", expiry); |
254 | } | 269 | } |
255 | else { | 270 | else { |
256 | appendFormat_String(&item.meta, | 271 | appendFormat_String(&item->meta, |
257 | " \u2014 %s\n%s%s", | 272 | " \u2014 %s\n%s%s", |
258 | expiry, | 273 | expiry, |
259 | escape_Color(uiHeading_ColorId), | 274 | escape_Color(uiHeading_ColorId), |
260 | cstr_String(&ident->notes)); | 275 | cstr_String(&ident->notes)); |
261 | } | 276 | } |
262 | item.isSelected = isActive; | 277 | item->listItem.isSelected = isActive; |
263 | pushBack_Array(&d->items, &item); | 278 | //pushBack_Array(&d->items, &item); |
279 | addItem_ListWidget(d->list, item); | ||
280 | iRelease(item); | ||
264 | } | 281 | } |
265 | const iMenuItem menuItems[] = { | 282 | const iMenuItem menuItems[] = { |
266 | { "Use on This Page", 0, 0, "ident.use arg:1" }, | 283 | { "Use on This Page", 0, 0, "ident.use arg:1" }, |
@@ -280,39 +297,38 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { | |||
280 | default: | 297 | default: |
281 | break; | 298 | break; |
282 | } | 299 | } |
283 | updateVisible_SidebarWidget_(d); | 300 | updateVisible_ListWidget(d->list); |
284 | invalidate_SidebarWidget_(d); | 301 | invalidate_ListWidget(d->list); |
285 | } | 302 | } |
286 | 303 | ||
287 | static void scroll_SidebarWidget_(iSidebarWidget *d, int offset) { | 304 | //static void scroll_SidebarWidget_(iSidebarWidget *d, int offset) { |
288 | const int oldScroll = d->scrollY; | 305 | // const int oldScroll = d->scrollY; |
289 | d->scrollY += offset; | 306 | // d->scrollY += offset; |
290 | if (d->scrollY < 0) { | 307 | // if (d->scrollY < 0) { |
291 | d->scrollY = 0; | 308 | // d->scrollY = 0; |
292 | } | 309 | // } |
293 | const int scrollMax = scrollMax_SidebarWidget_(d); | 310 | // const int scrollMax = scrollMax_SidebarWidget_(d); |
294 | d->scrollY = iMin(d->scrollY, scrollMax); | 311 | // d->scrollY = iMin(d->scrollY, scrollMax); |
295 | if (oldScroll != d->scrollY) { | 312 | // if (oldScroll != d->scrollY) { |
296 | d->hoverItem = iInvalidPos; | 313 | // d->hoverItem = iInvalidPos; |
297 | updateVisible_SidebarWidget_(d); | 314 | // updateVisible_SidebarWidget_(d); |
298 | invalidate_SidebarWidget_(d); | 315 | // invalidate_SidebarWidget_(d); |
299 | } | 316 | // } |
300 | } | 317 | //} |
301 | 318 | ||
302 | void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | 319 | void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { |
303 | if (d->mode == mode) return; | 320 | if (d->mode == mode) return; |
304 | if (d->mode >= 0 && d->mode < max_SidebarMode) { | 321 | if (d->mode >= 0 && d->mode < max_SidebarMode) { |
305 | d->modeScroll[d->mode] = d->scrollY; /* saved for later */ | 322 | d->modeScroll[d->mode] = scrollPos_ListWidget(d->list); /* saved for later */ |
306 | } | 323 | } |
307 | d->mode = mode; | 324 | d->mode = mode; |
308 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { | 325 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { |
309 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); | 326 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); |
310 | } | 327 | } |
311 | const float heights[max_SidebarMode] = { 1.333f, 1.333f, 3.5f, 1.2f }; | 328 | const float heights[max_SidebarMode] = { 1.333f, 1.333f, 3.5f, 1.2f }; |
312 | d->itemHeight = heights[mode] * lineHeight_Text(uiContent_FontId); | 329 | setItemHeight_ListWidget(d->list, heights[mode] * lineHeight_Text(uiContent_FontId)); |
313 | invalidate_SidebarWidget_(d); | ||
314 | /* Restore previous scroll position. */ | 330 | /* Restore previous scroll position. */ |
315 | d->scrollY = d->modeScroll[mode]; | 331 | setScrollPos_ListWidget(d->list, d->modeScroll[mode]); |
316 | } | 332 | } |
317 | 333 | ||
318 | enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { | 334 | enum iSidebarMode mode_SidebarWidget(const iSidebarWidget *d) { |
@@ -343,18 +359,15 @@ void init_SidebarWidget(iSidebarWidget *d) { | |||
343 | setId_Widget(w, "sidebar"); | 359 | setId_Widget(w, "sidebar"); |
344 | setBackgroundColor_Widget(w, none_ColorId); | 360 | setBackgroundColor_Widget(w, none_ColorId); |
345 | setFlags_Widget(w, | 361 | setFlags_Widget(w, |
346 | hidden_WidgetFlag | hover_WidgetFlag | arrangeHorizontal_WidgetFlag | | 362 | hidden_WidgetFlag | arrangeVertical_WidgetFlag | |
347 | resizeWidthOfChildren_WidgetFlag | collapse_WidgetFlag, | 363 | resizeChildren_WidgetFlag | collapse_WidgetFlag, |
348 | iTrue); | 364 | iTrue); |
349 | d->scrollY = 0; | ||
350 | iZap(d->modeScroll); | 365 | iZap(d->modeScroll); |
351 | d->mode = -1; | 366 | d->mode = -1; |
352 | d->width = 60 * gap_UI; | 367 | d->width = 60 * gap_UI; |
353 | init_Array(&d->items, sizeof(iSidebarItem)); | ||
354 | d->hoverItem = iInvalidPos; | ||
355 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | ||
356 | setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); | 368 | setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); |
357 | d->maxButtonLabelWidth = 0; | 369 | d->maxButtonLabelWidth = 0; |
370 | /* TODO: Add a parent h-div for the mode buttons. */ | ||
358 | for (int i = 0; i < max_SidebarMode; i++) { | 371 | for (int i = 0; i < max_SidebarMode; i++) { |
359 | d->modeButtons[i] = addChildFlags_Widget( | 372 | d->modeButtons[i] = addChildFlags_Widget( |
360 | w, | 373 | w, |
@@ -365,8 +378,7 @@ void init_SidebarWidget(iSidebarWidget *d) { | |||
365 | iMaxi(d->maxButtonLabelWidth, | 378 | iMaxi(d->maxButtonLabelWidth, |
366 | 3 * gap_UI + measure_Text(uiLabel_FontId, normalModeLabels_[i]).x); | 379 | 3 * gap_UI + measure_Text(uiLabel_FontId, normalModeLabels_[i]).x); |
367 | } | 380 | } |
368 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 381 | addChildFlags_Widget(w, iClob(d->list = new_ListWidget()), expand_WidgetFlag); |
369 | setThumb_ScrollWidget(d->scroll, 0, 0); | ||
370 | setMode_SidebarWidget(d, documentOutline_SidebarMode); | 382 | setMode_SidebarWidget(d, documentOutline_SidebarMode); |
371 | d->resizer = addChildFlags_Widget( | 383 | d->resizer = addChildFlags_Widget( |
372 | w, | 384 | w, |
@@ -378,55 +390,51 @@ void init_SidebarWidget(iSidebarWidget *d) { | |||
378 | setBackgroundColor_Widget(d->resizer, none_ColorId); | 390 | setBackgroundColor_Widget(d->resizer, none_ColorId); |
379 | d->resizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); | 391 | d->resizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); |
380 | d->menu = NULL; | 392 | d->menu = NULL; |
381 | init_IntSet(&d->invalidItems); | ||
382 | d->visBuffer = NULL; | ||
383 | d->visBufferValid = iFalse; | ||
384 | } | 393 | } |
385 | 394 | ||
386 | void deinit_SidebarWidget(iSidebarWidget *d) { | 395 | void deinit_SidebarWidget(iSidebarWidget *d) { |
387 | SDL_FreeCursor(d->resizeCursor); | 396 | SDL_FreeCursor(d->resizeCursor); |
388 | clearItems_SidebarWidget_(d); | 397 | //clearItems_SidebarWidget_(d); |
389 | deinit_Array(&d->items); | 398 | // deinit_Array(&d->items); |
390 | SDL_DestroyTexture(d->visBuffer); | ||
391 | } | 399 | } |
392 | 400 | ||
393 | static int visCount_SidebarWidget_(const iSidebarWidget *d) { | 401 | //static int visCount_SidebarWidget_(const iSidebarWidget *d) { |
394 | return iMin(height_Rect(bounds_Widget(constAs_Widget(d))) / d->itemHeight, | 402 | // return iMin(height_Rect(bounds_Widget(constAs_Widget(d))) / d->itemHeight, |
395 | (int) size_Array(&d->items)); | 403 | // (int) size_Array(&d->items)); |
396 | } | 404 | //} |
397 | 405 | ||
398 | static iRanges visRange_SidebarWidget_(const iSidebarWidget *d) { | 406 | //static iRanges visRange_SidebarWidget_(const iSidebarWidget *d) { |
399 | iRanges vis = { d->scrollY / d->itemHeight, 0 }; | 407 | // iRanges vis = { d->scrollY / d->itemHeight, 0 }; |
400 | vis.end = iMin(size_Array(&d->items), vis.start + visCount_SidebarWidget_(d)); | 408 | // vis.end = iMin(size_Array(&d->items), vis.start + visCount_SidebarWidget_(d)); |
401 | return vis; | 409 | // return vis; |
402 | } | 410 | //} |
403 | 411 | ||
404 | static size_t itemIndex_SidebarWidget_(const iSidebarWidget *d, iInt2 pos) { | 412 | //static size_t itemIndex_SidebarWidget_(const iSidebarWidget *d, iInt2 pos) { |
405 | const iRect bounds = contentBounds_SidebarWidget_(d); | 413 | // const iRect bounds = contentBounds_SidebarWidget_(d); |
406 | pos.y -= top_Rect(bounds) - d->scrollY; | 414 | // pos.y -= top_Rect(bounds) - d->scrollY; |
407 | if (pos.y < 0) return iInvalidPos; | 415 | // if (pos.y < 0) return iInvalidPos; |
408 | size_t index = pos.y / d->itemHeight; | 416 | // size_t index = pos.y / d->itemHeight; |
409 | if (index >= size_Array(&d->items)) return iInvalidPos; | 417 | // if (index >= size_Array(&d->items)) return iInvalidPos; |
410 | return index; | 418 | // return index; |
411 | } | 419 | //} |
412 | 420 | ||
413 | static const iSidebarItem *constHoverItem_SidebarWidget_(const iSidebarWidget *d) { | 421 | //static const iSidebarItem *constHoverItem_SidebarWidget_(const iSidebarWidget *d) { |
414 | if (d->hoverItem < size_Array(&d->items)) { | 422 | // if (d->hoverItem < size_Array(&d->items)) { |
415 | return constAt_Array(&d->items, d->hoverItem); | 423 | // return constAt_Array(&d->items, d->hoverItem); |
416 | } | 424 | // } |
417 | return NULL; | 425 | // return NULL; |
418 | } | 426 | //} |
419 | 427 | ||
420 | static iSidebarItem *hoverItem_SidebarWidget_(iSidebarWidget *d) { | 428 | //static iSidebarItem *hoverItem_SidebarWidget_(iSidebarWidget *d) { |
421 | if (d->hoverItem < size_Array(&d->items)) { | 429 | // if (d->hoverItem < size_Array(&d->items)) { |
422 | return at_Array(&d->items, d->hoverItem); | 430 | // return at_Array(&d->items, d->hoverItem); |
423 | } | 431 | // } |
424 | return NULL; | 432 | // return NULL; |
425 | } | 433 | //} |
426 | 434 | ||
427 | static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { | 435 | static const iGmIdentity *constHoverIdentity_SidebarWidget_(const iSidebarWidget *d) { |
428 | if (d->mode == identities_SidebarMode) { | 436 | if (d->mode == identities_SidebarMode) { |
429 | const iSidebarItem *hoverItem = constHoverItem_SidebarWidget_(d); | 437 | const iSidebarItem *hoverItem = constHoverItem_ListWidget(d->list); |
430 | if (hoverItem) { | 438 | if (hoverItem) { |
431 | return identity_GmCerts(certs_App(), hoverItem->id); | 439 | return identity_GmCerts(certs_App(), hoverItem->id); |
432 | } | 440 | } |
@@ -438,28 +446,28 @@ static iGmIdentity *hoverIdentity_SidebarWidget_(const iSidebarWidget *d) { | |||
438 | return iConstCast(iGmIdentity *, constHoverIdentity_SidebarWidget_(d)); | 446 | return iConstCast(iGmIdentity *, constHoverIdentity_SidebarWidget_(d)); |
439 | } | 447 | } |
440 | 448 | ||
441 | static void setHoverItem_SidebarWidget_(iSidebarWidget *d, size_t index) { | 449 | //static void setHoverItem_SidebarWidget_(iSidebarWidget *d, size_t index) { |
442 | if (index < size_Array(&d->items)) { | 450 | // if (index < size_Array(&d->items)) { |
443 | if (constValue_Array(&d->items, index, iSidebarItem).isSeparator) { | 451 | // if (constValue_Array(&d->items, index, iSidebarItem).isSeparator) { |
444 | index = iInvalidPos; | 452 | // index = iInvalidPos; |
445 | } | 453 | // } |
446 | } | 454 | // } |
447 | if (d->hoverItem != index) { | 455 | // if (d->hoverItem != index) { |
448 | insert_IntSet(&d->invalidItems, d->hoverItem); | 456 | // insert_IntSet(&d->invalidItems, d->hoverItem); |
449 | insert_IntSet(&d->invalidItems, index); | 457 | // insert_IntSet(&d->invalidItems, index); |
450 | d->hoverItem = index; | 458 | // d->hoverItem = index; |
451 | refresh_Widget(as_Widget(d)); | 459 | // refresh_Widget(as_Widget(d)); |
452 | } | 460 | // } |
453 | } | 461 | //} |
454 | 462 | ||
455 | static void updateMouseHover_SidebarWidget_(iSidebarWidget *d) { | 463 | //static void updateMouseHover_SidebarWidget_(iSidebarWidget *d) { |
456 | const iInt2 mouse = mouseCoord_Window(get_Window()); | 464 | // const iInt2 mouse = mouseCoord_Window(get_Window()); |
457 | setHoverItem_SidebarWidget_(d, itemIndex_SidebarWidget_(d, mouse)); | 465 | // setHoverItem_SidebarWidget_(d, itemIndex_SidebarWidget_(d, mouse)); |
458 | } | 466 | //} |
459 | 467 | ||
460 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { | 468 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, const iSidebarItem *item) { |
461 | setFocus_Widget(NULL); | 469 | setFocus_Widget(NULL); |
462 | const iSidebarItem *item = constAt_Array(&d->items, index); | 470 | // const iSidebarItem *item = constAt_Array(&d->items, index); |
463 | switch (d->mode) { | 471 | switch (d->mode) { |
464 | case documentOutline_SidebarMode: { | 472 | case documentOutline_SidebarMode: { |
465 | const iGmDocument *doc = document_DocumentWidget(document_App()); | 473 | const iGmDocument *doc = document_DocumentWidget(document_App()); |
@@ -485,7 +493,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { | |||
485 | signIn_GmCerts(certs_App(), ident, tabUrl); | 493 | signIn_GmCerts(certs_App(), ident, tabUrl); |
486 | } | 494 | } |
487 | updateItems_SidebarWidget_(d); | 495 | updateItems_SidebarWidget_(d); |
488 | updateMouseHover_SidebarWidget_(d); | 496 | updateMouseHover_ListWidget(d->list); |
489 | } | 497 | } |
490 | break; | 498 | break; |
491 | } | 499 | } |
@@ -520,7 +528,7 @@ void setWidth_SidebarWidget(iSidebarWidget *d, int width) { | |||
520 | checkModeButtonLayout_SidebarWidget_(d); | 528 | checkModeButtonLayout_SidebarWidget_(d); |
521 | if (!isRefreshPending_App()) { | 529 | if (!isRefreshPending_App()) { |
522 | updateSize_DocumentWidget(document_App()); | 530 | updateSize_DocumentWidget(document_App()); |
523 | invalidate_SidebarWidget_(d); | 531 | invalidate_ListWidget(d->list); |
524 | } | 532 | } |
525 | } | 533 | } |
526 | 534 | ||
@@ -531,7 +539,7 @@ iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *c | |||
531 | const iString *title = text_InputWidget(findChild_Widget(editor, "bmed.title")); | 539 | const iString *title = text_InputWidget(findChild_Widget(editor, "bmed.title")); |
532 | const iString *url = text_InputWidget(findChild_Widget(editor, "bmed.url")); | 540 | const iString *url = text_InputWidget(findChild_Widget(editor, "bmed.url")); |
533 | const iString *tags = text_InputWidget(findChild_Widget(editor, "bmed.tags")); | 541 | const iString *tags = text_InputWidget(findChild_Widget(editor, "bmed.tags")); |
534 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 542 | const iSidebarItem *item = hoverItem_ListWidget(d->list); |
535 | iAssert(item); /* hover item cannot have been changed */ | 543 | iAssert(item); /* hover item cannot have been changed */ |
536 | iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); | 544 | iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); |
537 | set_String(&bm->title, title); | 545 | set_String(&bm->title, title); |
@@ -550,9 +558,9 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
550 | iWidget *w = as_Widget(d); | 558 | iWidget *w = as_Widget(d); |
551 | /* Handle commands. */ | 559 | /* Handle commands. */ |
552 | if (isResize_UserEvent(ev)) { | 560 | if (isResize_UserEvent(ev)) { |
553 | updateVisible_SidebarWidget_(d); | 561 | updateVisible_ListWidget(d->list); |
554 | checkModeButtonLayout_SidebarWidget_(d); | 562 | checkModeButtonLayout_SidebarWidget_(d); |
555 | invalidate_SidebarWidget_(d); | 563 | invalidate_ListWidget(d->list); |
556 | } | 564 | } |
557 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | 565 | else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { |
558 | const char *cmd = command_UserEvent(ev); | 566 | const char *cmd = command_UserEvent(ev); |
@@ -580,6 +588,10 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
580 | } | 588 | } |
581 | return iTrue; | 589 | return iTrue; |
582 | } | 590 | } |
591 | else if (isCommand_Widget(w, ev, "list.clicked")) { | ||
592 | itemClicked_SidebarWidget_(d, pointerLabel_Command(cmd, "item")); | ||
593 | return iTrue; | ||
594 | } | ||
583 | else if (equal_Command(cmd, "sidebar.width")) { | 595 | else if (equal_Command(cmd, "sidebar.width")) { |
584 | setWidth_SidebarWidget(d, arg_Command(cmd)); | 596 | setWidth_SidebarWidget(d, arg_Command(cmd)); |
585 | return iTrue; | 597 | return iTrue; |
@@ -590,7 +602,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
590 | if (argLabel_Command(cmd, "show") && !isVisible_Widget(w)) { | 602 | if (argLabel_Command(cmd, "show") && !isVisible_Widget(w)) { |
591 | postCommand_App("sidebar.toggle arg:1"); | 603 | postCommand_App("sidebar.toggle arg:1"); |
592 | } | 604 | } |
593 | scroll_SidebarWidget_(d, 0); | 605 | scrollOffset_ListWidget(d->list, 0); |
594 | return iTrue; | 606 | return iTrue; |
595 | } | 607 | } |
596 | else if (equal_Command(cmd, "sidebar.toggle")) { | 608 | else if (equal_Command(cmd, "sidebar.toggle")) { |
@@ -600,34 +612,25 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
600 | setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); | 612 | setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); |
601 | if (isVisible_Widget(w)) { | 613 | if (isVisible_Widget(w)) { |
602 | w->rect.size.x = d->width; | 614 | w->rect.size.x = d->width; |
603 | invalidate_SidebarWidget_(d); | 615 | invalidate_ListWidget(d->list); |
604 | } | 616 | } |
605 | arrange_Widget(w->parent); | 617 | arrange_Widget(w->parent); |
606 | updateSize_DocumentWidget(document_App()); | 618 | updateSize_DocumentWidget(document_App()); |
607 | refresh_Widget(w->parent); | 619 | refresh_Widget(w->parent); |
608 | return iTrue; | 620 | return iTrue; |
609 | } | 621 | } |
610 | else if (equal_Command(cmd, "scroll.moved")) { | ||
611 | d->scrollY = arg_Command(command_UserEvent(ev)); | ||
612 | d->hoverItem = iInvalidPos; | ||
613 | invalidate_SidebarWidget_(d); | ||
614 | return iTrue; | ||
615 | } | ||
616 | else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { | 622 | else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { |
617 | updateItems_SidebarWidget_(d); | 623 | updateItems_SidebarWidget_(d); |
618 | } | 624 | } |
619 | else if (equal_Command(cmd, "theme.changed")) { | ||
620 | invalidate_SidebarWidget_(d); | ||
621 | } | ||
622 | else if (equal_Command(cmd, "bookmark.copy")) { | 625 | else if (equal_Command(cmd, "bookmark.copy")) { |
623 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 626 | const iSidebarItem *item = hoverItem_ListWidget(d->list); |
624 | if (d->mode == bookmarks_SidebarMode && item) { | 627 | if (d->mode == bookmarks_SidebarMode && item) { |
625 | SDL_SetClipboardText(cstr_String(&item->url)); | 628 | SDL_SetClipboardText(cstr_String(&item->url)); |
626 | } | 629 | } |
627 | return iTrue; | 630 | return iTrue; |
628 | } | 631 | } |
629 | else if (equal_Command(cmd, "bookmark.edit")) { | 632 | else if (equal_Command(cmd, "bookmark.edit")) { |
630 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 633 | const iSidebarItem *item = hoverItem_ListWidget(d->list); |
631 | if (d->mode == bookmarks_SidebarMode && item) { | 634 | if (d->mode == bookmarks_SidebarMode && item) { |
632 | setFlags_Widget(w, disabled_WidgetFlag, iTrue); | 635 | setFlags_Widget(w, disabled_WidgetFlag, iTrue); |
633 | iWidget *dlg = makeBookmarkEditor_Widget(); | 636 | iWidget *dlg = makeBookmarkEditor_Widget(); |
@@ -641,7 +644,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
641 | return iTrue; | 644 | return iTrue; |
642 | } | 645 | } |
643 | else if (equal_Command(cmd, "bookmark.delete")) { | 646 | else if (equal_Command(cmd, "bookmark.delete")) { |
644 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 647 | const iSidebarItem *item = hoverItem_ListWidget(d->list); |
645 | if (d->mode == bookmarks_SidebarMode && item && remove_Bookmarks(bookmarks_App(), item->id)) { | 648 | if (d->mode == bookmarks_SidebarMode && item && remove_Bookmarks(bookmarks_App(), item->id)) { |
646 | postCommand_App("bookmarks.changed"); | 649 | postCommand_App("bookmarks.changed"); |
647 | } | 650 | } |
@@ -712,7 +715,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
712 | return iTrue; | 715 | return iTrue; |
713 | } | 716 | } |
714 | else if (equal_Command(cmd, "ident.delete")) { | 717 | else if (equal_Command(cmd, "ident.delete")) { |
715 | iSidebarItem *item = hoverItem_SidebarWidget_(d); | 718 | iSidebarItem *item = hoverItem_ListWidget(d->list); |
716 | if (argLabel_Command(cmd, "confirm")) { | 719 | if (argLabel_Command(cmd, "confirm")) { |
717 | makeQuestion_Widget(uiTextCaution_ColorEscape "DELETE IDENTITY", | 720 | makeQuestion_Widget(uiTextCaution_ColorEscape "DELETE IDENTITY", |
718 | format_CStr("Do you really want to delete the identity\n" | 721 | 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) | |||
732 | return iTrue; | 735 | return iTrue; |
733 | } | 736 | } |
734 | else if (equal_Command(cmd, "history.delete")) { | 737 | else if (equal_Command(cmd, "history.delete")) { |
735 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 738 | const iSidebarItem *item = hoverItem_ListWidget(d->list); |
736 | if (item && !isEmpty_String(&item->url)) { | 739 | if (item && !isEmpty_String(&item->url)) { |
737 | removeUrl_Visited(visited_App(), &item->url); | 740 | removeUrl_Visited(visited_App(), &item->url); |
738 | updateItems_SidebarWidget_(d); | 741 | updateItems_SidebarWidget_(d); |
739 | scroll_SidebarWidget_(d, 0); | 742 | scrollOffset_ListWidget(d->list, 0); |
740 | } | 743 | } |
741 | return iTrue; | 744 | return iTrue; |
742 | } | 745 | } |
743 | else if (equal_Command(cmd, "history.copy")) { | 746 | else if (equal_Command(cmd, "history.copy")) { |
744 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 747 | const iSidebarItem *item = hoverItem_ListWidget(d->list); |
745 | if (item && !isEmpty_String(&item->url)) { | 748 | if (item && !isEmpty_String(&item->url)) { |
746 | SDL_SetClipboardText(cstr_String(&item->url)); | 749 | SDL_SetClipboardText(cstr_String(&item->url)); |
747 | } | 750 | } |
748 | return iTrue; | 751 | return iTrue; |
749 | } | 752 | } |
750 | else if (equal_Command(cmd, "history.addbookmark")) { | 753 | else if (equal_Command(cmd, "history.addbookmark")) { |
751 | const iSidebarItem *item = hoverItem_SidebarWidget_(d); | 754 | const iSidebarItem *item = hoverItem_ListWidget(d->list); |
752 | if (!isEmpty_String(&item->url)) { | 755 | if (!isEmpty_String(&item->url)) { |
753 | makeBookmarkCreation_Widget( | 756 | makeBookmarkCreation_Widget( |
754 | &item->url, | 757 | &item->url, |
@@ -768,53 +771,55 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
768 | else { | 771 | else { |
769 | clear_Visited(visited_App()); | 772 | clear_Visited(visited_App()); |
770 | updateItems_SidebarWidget_(d); | 773 | updateItems_SidebarWidget_(d); |
771 | scroll_SidebarWidget_(d, 0); | 774 | scrollOffset_ListWidget(d->list, 0); |
772 | } | 775 | } |
773 | return iTrue; | 776 | return iTrue; |
774 | } | 777 | } |
775 | } | 778 | } |
776 | if (ev->type == SDL_MOUSEMOTION && !isVisible_Widget(d->menu)) { | 779 | if (ev->type == SDL_MOUSEMOTION && !isVisible_Widget(d->menu)) { |
777 | const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); | 780 | const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); |
778 | size_t hover = iInvalidPos; | 781 | // size_t hover = iInvalidPos; |
779 | if (contains_Widget(d->resizer, mouse)) { | 782 | if (contains_Widget(d->resizer, mouse)) { |
780 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_SIZEWE); | 783 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_SIZEWE); |
781 | } | 784 | } |
782 | else if (contains_Widget(constAs_Widget(d->scroll), mouse)) { | 785 | // else if (contains_Widget(constAs_Widget(d->scroll), mouse)) { |
783 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); | 786 | // setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); |
784 | } | 787 | // } |
785 | else if (contains_Widget(w, mouse)) { | 788 | // else if (contains_Widget(w, mouse)) { |
786 | hover = itemIndex_SidebarWidget_(d, mouse); | 789 | // hover = itemIndex_SidebarWidget_(d, mouse); |
787 | } | 790 | // } |
788 | setHoverItem_SidebarWidget_(d, hover); | 791 | // setHoverItem_SidebarWidget_(d, hover); |
789 | /* Update cursor. */ | 792 | /* Update cursor. */ |
790 | if (contains_Widget(w, mouse) && !contains_Widget(d->resizer, mouse) && | 793 | else if (contains_Widget(w, mouse)) /* && !contains_Widget(d->resizer, mouse) && |
791 | !contains_Widget(constAs_Widget(d->scroll), mouse)) { | 794 | !contains_Widget(constAs_Widget(d->scroll), mouse))*/ { |
792 | const iSidebarItem *item = constHoverItem_SidebarWidget_(d); | 795 | const iSidebarItem *item = constHoverItem_ListWidget(d->list); |
793 | if (item && d->mode != identities_SidebarMode) { | 796 | if (item && d->mode != identities_SidebarMode) { |
794 | setCursor_Window(get_Window(), item->isSeparator ? SDL_SYSTEM_CURSOR_ARROW | 797 | setCursor_Window(get_Window(), |
795 | : SDL_SYSTEM_CURSOR_HAND); | 798 | item->listItem.isSeparator ? SDL_SYSTEM_CURSOR_ARROW |
799 | : SDL_SYSTEM_CURSOR_HAND); | ||
796 | } | 800 | } |
797 | else { | 801 | else { |
798 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); | 802 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); |
799 | } | 803 | } |
800 | } | 804 | } |
801 | } | 805 | } |
802 | if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { | 806 | // if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { |
803 | #if defined (iPlatformApple) | 807 | //#if defined (iPlatformApple) |
804 | /* Momentum scrolling. */ | 808 | // /* Momentum scrolling. */ |
805 | scroll_SidebarWidget_(d, -ev->wheel.y * get_Window()->pixelRatio); | 809 | // scroll_SidebarWidget_(d, -ev->wheel.y * get_Window()->pixelRatio); |
806 | #else | 810 | //#else |
807 | scroll_SidebarWidget_(d, -ev->wheel.y * 3 * d->itemHeight); | 811 | // scroll_SidebarWidget_(d, -ev->wheel.y * 3 * d->itemHeight); |
808 | #endif | 812 | //#endif |
809 | return iTrue; | 813 | // return iTrue; |
810 | } | 814 | // } |
811 | if (d->menu && ev->type == SDL_MOUSEBUTTONDOWN) { | 815 | if (d->menu && ev->type == SDL_MOUSEBUTTONDOWN) { |
812 | if (ev->button.button == SDL_BUTTON_RIGHT) { | 816 | if (ev->button.button == SDL_BUTTON_RIGHT) { |
813 | if (!isVisible_Widget(d->menu)) { | 817 | if (!isVisible_Widget(d->menu)) { |
814 | setHoverItem_SidebarWidget_( | 818 | updateMouseHover_ListWidget(d->list); |
815 | d, itemIndex_SidebarWidget_(d, init_I2(ev->button.x, ev->button.y))); | 819 | // setHoverItem_ListWidget( |
820 | // d->list, itemIndex_ListWidget(d->list, init_I2(ev->button.x, ev->button.y))); | ||
816 | } | 821 | } |
817 | if (d->hoverItem != iInvalidPos || isVisible_Widget(d->menu)) { | 822 | if (constHoverItem_ListWidget(d->list) || isVisible_Widget(d->menu)) { |
818 | /* Update menu items. */ | 823 | /* Update menu items. */ |
819 | if (d->mode == identities_SidebarMode) { | 824 | if (d->mode == identities_SidebarMode) { |
820 | const iGmIdentity *ident = constHoverIdentity_SidebarWidget_(d); | 825 | const iGmIdentity *ident = constHoverIdentity_SidebarWidget_(d); |
@@ -845,41 +850,27 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
845 | } | 850 | } |
846 | } | 851 | } |
847 | processContextMenuEvent_Widget(d->menu, ev, {}); | 852 | processContextMenuEvent_Widget(d->menu, ev, {}); |
848 | switch (processEvent_Click(&d->click, ev)) { | 853 | // switch (processEvent_Click(&d->click, ev)) { |
849 | case started_ClickResult: | 854 | // case started_ClickResult: |
850 | //invalidate_SidebarWidget_(d); | 855 | // //invalidate_SidebarWidget_(d); |
851 | break; | 856 | // break; |
852 | case finished_ClickResult: | 857 | // case finished_ClickResult: |
853 | if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && | 858 | // if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && |
854 | d->hoverItem != iInvalidSize) { | 859 | // d->hoverItem != iInvalidSize) { |
855 | itemClicked_SidebarWidget_(d, d->hoverItem); | 860 | // itemClicked_SidebarWidget_(d, d->hoverItem); |
856 | } | 861 | // } |
857 | // invalidate_SidebarWidget_(d); | 862 | // // invalidate_SidebarWidget_(d); |
858 | break; | 863 | // break; |
859 | default: | 864 | // default: |
860 | break; | 865 | // break; |
861 | } | 866 | // } |
862 | return processEvent_Widget(w, ev); | 867 | return processEvent_Widget(w, ev); |
863 | } | 868 | } |
864 | 869 | ||
865 | static void allocVisBuffer_SidebarWidget_(iSidebarWidget *d) { | ||
866 | const iInt2 size = contentBounds_SidebarWidget_(d).size; | ||
867 | if (!d->visBuffer || !isEqual_I2(size_SDLTexture(d->visBuffer), size)) { | ||
868 | if (d->visBuffer) { | ||
869 | SDL_DestroyTexture(d->visBuffer); | ||
870 | } | ||
871 | d->visBuffer = SDL_CreateTexture(renderer_Window(get_Window()), | ||
872 | SDL_PIXELFORMAT_RGBA8888, | ||
873 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
874 | size.x, | ||
875 | size.y); | ||
876 | SDL_SetTextureBlendMode(d->visBuffer, SDL_BLENDMODE_NONE); | ||
877 | d->visBufferValid = iFalse; | ||
878 | } | ||
879 | } | ||
880 | |||
881 | static void draw_SidebarWidget_(const iSidebarWidget *d) { | 870 | static void draw_SidebarWidget_(const iSidebarWidget *d) { |
882 | const iWidget *w = constAs_Widget(d); | 871 | const iWidget *w = constAs_Widget(d); |
872 | const iRect bounds = bounds_Widget(w); | ||
873 | #if 0 | ||
883 | const iRect bounds = contentBounds_SidebarWidget_(d); | 874 | const iRect bounds = contentBounds_SidebarWidget_(d); |
884 | const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); | 875 | const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); |
885 | iPaint p; | 876 | iPaint p; |
@@ -1033,18 +1024,124 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { | |||
1033 | } | 1024 | } |
1034 | endTarget_Paint(&p); | 1025 | endTarget_Paint(&p); |
1035 | /* Update state. */ { | 1026 | /* Update state. */ { |
1036 | iSidebarWidget *m = iConstCast(iSidebarWidget *, d); | 1027 | // iSidebarWidget *m = iConstCast(iSidebarWidget *, d); |
1037 | m->visBufferValid = iTrue; | 1028 | // m->visBufferValid = iTrue; |
1038 | clear_IntSet(&m->invalidItems); | 1029 | // clear_IntSet(&m->invalidItems); |
1039 | } | 1030 | } |
1040 | } | 1031 | } |
1041 | SDL_RenderCopy( | 1032 | SDL_RenderCopy( |
1042 | renderer_Window(get_Window()), d->visBuffer, NULL, (const SDL_Rect *) &bounds); | 1033 | renderer_Window(get_Window()), d->visBuffer, NULL, (const SDL_Rect *) &bounds); |
1034 | #endif | ||
1035 | iPaint p; | ||
1036 | init_Paint(&p); | ||
1043 | draw_Widget(w); | 1037 | draw_Widget(w); |
1044 | drawVLine_Paint(&p, | 1038 | drawVLine_Paint( |
1045 | addX_I2(topRight_Rect(bounds_Widget(w)), -1), | 1039 | &p, addX_I2(topRight_Rect(bounds), -1), height_Rect(bounds), uiSeparator_ColorId); |
1046 | height_Rect(bounds_Widget(w)), | 1040 | } |
1047 | uiSeparator_ColorId); | 1041 | |
1042 | static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, | ||
1043 | const iListWidget *list) { | ||
1044 | const iSidebarWidget *sidebar = (const iSidebarWidget *) constAs_Widget(list)->parent; | ||
1045 | const iBool isPressing = isMouseDown_ListWidget(list); | ||
1046 | const int font = uiContent_FontId; | ||
1047 | const iBool isHover = | ||
1048 | isHover_Widget(constAs_Widget(list)) && constHoverItem_ListWidget(list) == d; | ||
1049 | const int itemHeight = height_Rect(itemRect); | ||
1050 | const int iconColor = | ||
1051 | isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) : uiIcon_ColorId; | ||
1052 | if (isHover && !d->listItem.isSeparator) { | ||
1053 | fillRect_Paint(p, | ||
1054 | itemRect, | ||
1055 | isPressing ? uiBackgroundPressed_ColorId | ||
1056 | : uiBackgroundFramelessHover_ColorId); | ||
1057 | } | ||
1058 | iInt2 pos = itemRect.pos; | ||
1059 | if (sidebar->mode == documentOutline_SidebarMode) { | ||
1060 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
1061 | : (tmHeading1_ColorId + d->indent / (4 * gap_UI)); | ||
1062 | drawRange_Text(font, | ||
1063 | init_I2(pos.x + 3 * gap_UI + d->indent, | ||
1064 | mid_Rect(itemRect).y - lineHeight_Text(font) / 2), | ||
1065 | fg, | ||
1066 | range_String(&d->label)); | ||
1067 | } | ||
1068 | else if (sidebar->mode == bookmarks_SidebarMode) { | ||
1069 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
1070 | : uiText_ColorId; | ||
1071 | iString str; | ||
1072 | init_String(&str); | ||
1073 | appendChar_String(&str, d->icon ? d->icon : 0x1f588); | ||
1074 | const iRect iconArea = { addX_I2(pos, gap_UI), init_I2(7 * gap_UI, itemHeight) }; | ||
1075 | drawCentered_Text(font, iconArea, iTrue, iconColor, "%s", cstr_String(&str)); | ||
1076 | deinit_String(&str); | ||
1077 | iInt2 textPos = addY_I2(topRight_Rect(iconArea), (itemHeight - lineHeight_Text(font)) / 2); | ||
1078 | drawRange_Text(font, textPos, fg, range_String(&d->label)); | ||
1079 | } | ||
1080 | else if (sidebar->mode == history_SidebarMode) { | ||
1081 | iBeginCollect(); | ||
1082 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
1083 | : uiText_ColorId; | ||
1084 | if (d->listItem.isSeparator) { | ||
1085 | if (!isEmpty_String(&d->meta)) { | ||
1086 | unsetClip_Paint(p); | ||
1087 | iInt2 drawPos = addY_I2(topLeft_Rect(itemRect), itemHeight * 0.666f); | ||
1088 | drawHLine_Paint(p, drawPos, width_Rect(itemRect), uiIcon_ColorId); | ||
1089 | drawRange_Text( | ||
1090 | default_FontId, | ||
1091 | add_I2(drawPos, | ||
1092 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(default_FontId)) / 2)), | ||
1093 | uiIcon_ColorId, | ||
1094 | range_String(&d->meta)); | ||
1095 | } | ||
1096 | } | ||
1097 | else { | ||
1098 | iUrl parts; | ||
1099 | init_Url(&parts, &d->url); | ||
1100 | const iBool isGemini = equalCase_Rangecc(parts.scheme, "gemini"); | ||
1101 | draw_Text(font, | ||
1102 | add_I2(topLeft_Rect(itemRect), | ||
1103 | init_I2(3 * gap_UI, (itemHeight - lineHeight_Text(font)) / 2)), | ||
1104 | fg, | ||
1105 | "%s%s%s%s%s%s", | ||
1106 | isGemini ? "" : cstr_Rangecc(parts.scheme), | ||
1107 | isGemini ? "" : "://", | ||
1108 | escape_Color(isHover ? (isPressing ? uiTextPressed_ColorId | ||
1109 | : uiTextFramelessHover_ColorId) | ||
1110 | : uiTextStrong_ColorId), | ||
1111 | cstr_Rangecc(parts.host), | ||
1112 | escape_Color(fg), | ||
1113 | cstr_Rangecc(parts.path)); | ||
1114 | } | ||
1115 | iEndCollect(); | ||
1116 | } | ||
1117 | else if (sidebar->mode == identities_SidebarMode) { | ||
1118 | const int fg = isHover ? (isPressing ? uiTextPressed_ColorId : uiTextFramelessHover_ColorId) | ||
1119 | : uiTextStrong_ColorId; | ||
1120 | if (d->listItem.isSelected) { | ||
1121 | drawRectThickness_Paint(p, | ||
1122 | adjusted_Rect(itemRect, zero_I2(), init_I2(-2, -1)), | ||
1123 | gap_UI / 4, | ||
1124 | isHover && isPressing ? uiTextPressed_ColorId : uiIcon_ColorId); | ||
1125 | } | ||
1126 | iString icon; | ||
1127 | initUnicodeN_String(&icon, &d->icon, 1); | ||
1128 | iInt2 cPos = topLeft_Rect(itemRect); | ||
1129 | addv_I2(&cPos, | ||
1130 | init_I2(3 * gap_UI, | ||
1131 | (itemHeight - lineHeight_Text(default_FontId) * 2 - lineHeight_Text(font)) / | ||
1132 | 2)); | ||
1133 | const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId | ||
1134 | : uiTextFramelessHover_ColorId) | ||
1135 | : uiText_ColorId; | ||
1136 | drawRange_Text( | ||
1137 | font, cPos, d->listItem.isSelected ? iconColor : metaFg, range_String(&icon)); | ||
1138 | deinit_String(&icon); | ||
1139 | drawRange_Text(font, add_I2(cPos, init_I2(6 * gap_UI, 0)), fg, range_String(&d->label)); | ||
1140 | drawRange_Text(default_FontId, | ||
1141 | add_I2(cPos, init_I2(6 * gap_UI, lineHeight_Text(font))), | ||
1142 | metaFg, | ||
1143 | range_String(&d->meta)); | ||
1144 | } | ||
1048 | } | 1145 | } |
1049 | 1146 | ||
1050 | iBeginDefineSubclass(SidebarWidget, Widget) | 1147 | 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. */ | |||
36 | #include <the_Foundation/math.h> | 36 | #include <the_Foundation/math.h> |
37 | #include <the_Foundation/path.h> | 37 | #include <the_Foundation/path.h> |
38 | 38 | ||
39 | iBool isCommand_SDLEvent(const SDL_Event *d) { | ||
40 | return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode; | ||
41 | } | ||
42 | |||
39 | iBool isCommand_UserEvent(const SDL_Event *d, const char *cmd) { | 43 | iBool isCommand_UserEvent(const SDL_Event *d, const char *cmd) { |
40 | return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode && | 44 | return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode && |
41 | equal_Command(d->user.data1, cmd); | 45 | 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) | |||
32 | iDeclareType(Widget) | 32 | iDeclareType(Widget) |
33 | iDeclareType(LabelWidget) | 33 | iDeclareType(LabelWidget) |
34 | 34 | ||
35 | iBool isCommand_SDLEvent (const SDL_Event *d); | ||
35 | iBool isCommand_UserEvent (const SDL_Event *, const char *cmd); | 36 | iBool isCommand_UserEvent (const SDL_Event *, const char *cmd); |
36 | const char * command_UserEvent (const SDL_Event *); | 37 | const char * command_UserEvent (const SDL_Event *); |
37 | 38 | ||
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) { | |||
207 | height_Rect(d->rect) - d->padding[1] - d->padding[3]); | 207 | height_Rect(d->rect) - d->padding[1] - d->padding[3]); |
208 | } | 208 | } |
209 | 209 | ||
210 | iRect innerBounds_Widget(const iWidget *d) { | ||
211 | return adjusted_Rect(bounds_Widget(d), | ||
212 | init_I2(d->padding[0], d->padding[1]), | ||
213 | init_I2(-d->padding[2], -d->padding[3])); | ||
214 | } | ||
215 | |||
210 | void arrange_Widget(iWidget *d) { | 216 | void arrange_Widget(iWidget *d) { |
211 | if (isCollapsed_Widget_(d)) { | 217 | if (isCollapsed_Widget_(d)) { |
212 | setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue); | 218 | setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue); |