diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-11 16:10:42 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-11 16:10:42 +0300 |
commit | 1e8b17b775715edfc68157d9be6743109f65a71b (patch) | |
tree | 9680b21fed26866c1717bcf89f8ce38ec6047d52 /src/ui/sidebarwidget.c | |
parent | a0ddbf089bacfe8b9852e77a3bfcfb452d288053 (diff) |
Document outline; jumping to clicked heading
Diffstat (limited to 'src/ui/sidebarwidget.c')
-rw-r--r-- | src/ui/sidebarwidget.c | 227 |
1 files changed, 202 insertions, 25 deletions
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 8f1273bf..46b36434 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -1,57 +1,234 @@ | |||
1 | #include "sidebarwidget.h" | 1 | #include "sidebarwidget.h" |
2 | #include "labelwidget.h" | 2 | #include "labelwidget.h" |
3 | #include "scrollwidget.h" | 3 | #include "scrollwidget.h" |
4 | #include "documentwidget.h" | ||
4 | #include "paint.h" | 5 | #include "paint.h" |
5 | #include "util.h" | 6 | #include "util.h" |
7 | #include "command.h" | ||
8 | #include "../gmdocument.h" | ||
9 | #include "app.h" | ||
10 | |||
11 | #include <the_Foundation/array.h> | ||
12 | |||
13 | iDeclareType(SidebarItem) | ||
14 | |||
15 | struct Impl_SidebarItem { | ||
16 | int indent; | ||
17 | iChar icon; | ||
18 | iString label; | ||
19 | iString meta; | ||
20 | iString url; | ||
21 | size_t index; | ||
22 | }; | ||
23 | |||
24 | void init_SidebarItem(iSidebarItem *d) { | ||
25 | d->indent = 0; | ||
26 | d->icon = 0; | ||
27 | d->index = 0; | ||
28 | init_String(&d->label); | ||
29 | init_String(&d->meta); | ||
30 | init_String(&d->url); | ||
31 | } | ||
32 | |||
33 | void deinit_SidebarItem(iSidebarItem *d) { | ||
34 | deinit_String(&d->url); | ||
35 | deinit_String(&d->meta); | ||
36 | deinit_String(&d->label); | ||
37 | } | ||
38 | |||
39 | iDefineTypeConstruction(SidebarItem) | ||
40 | |||
41 | /*----------------------------------------------------------------------------------------------*/ | ||
6 | 42 | ||
7 | struct Impl_SidebarWidget { | 43 | struct Impl_SidebarWidget { |
8 | iWidget widget; | 44 | iWidget widget; |
9 | enum iSidebarMode mode; | 45 | enum iSidebarMode mode; |
10 | iScrollWidget *scroll; | 46 | iScrollWidget *scroll; |
47 | int scrollY; | ||
48 | iLabelWidget *modeButtons[max_SidebarMode]; | ||
49 | int itemHeight; | ||
50 | iArray items; | ||
51 | size_t hoverItem; | ||
52 | iClick click; | ||
11 | }; | 53 | }; |
12 | 54 | ||
13 | iDefineObjectConstruction(SidebarWidget) | 55 | iDefineObjectConstruction(SidebarWidget) |
14 | 56 | ||
57 | static void clearItems_SidebarWidget_(iSidebarWidget *d) { | ||
58 | iForEach(Array, i, &d->items) { | ||
59 | deinit_SidebarItem(i.value); | ||
60 | } | ||
61 | clear_Array(&d->items); | ||
62 | } | ||
63 | |||
64 | static void updateItems_SidebarWidget_(iSidebarWidget *d) { | ||
65 | clearItems_SidebarWidget_(d); | ||
66 | switch (d->mode) { | ||
67 | case documentOutline_SidebarMode: { | ||
68 | const iGmDocument *doc = document_DocumentWidget(document_App()); | ||
69 | iConstForEach(Array, i, headings_GmDocument(doc)) { | ||
70 | const iGmHeading *head = i.value; | ||
71 | iSidebarItem item; | ||
72 | init_SidebarItem(&item); | ||
73 | item.index = index_ArrayConstIterator(&i); | ||
74 | setRange_String(&item.label, head->text); | ||
75 | item.indent = head->level * 4 * gap_UI; | ||
76 | pushBack_Array(&d->items, &item); | ||
77 | } | ||
78 | break; | ||
79 | } | ||
80 | case bookmarks_SidebarMode: | ||
81 | break; | ||
82 | case history_SidebarMode: | ||
83 | break; | ||
84 | default: | ||
85 | break; | ||
86 | } | ||
87 | refresh_Widget(as_Widget(d)); | ||
88 | } | ||
89 | |||
90 | void setMode_SidebarWidget(iSidebarWidget *d, enum iSidebarMode mode) { | ||
91 | if (d->mode == mode) return; | ||
92 | d->mode = mode; | ||
93 | for (enum iSidebarMode i = 0; i < max_SidebarMode; i++) { | ||
94 | setFlags_Widget(as_Widget(d->modeButtons[i]), selected_WidgetFlag, i == d->mode); | ||
95 | } | ||
96 | const float heights[max_SidebarMode] = { 1.2f, 2, 3, 3 }; | ||
97 | d->itemHeight = heights[mode] * lineHeight_Text(default_FontId); | ||
98 | } | ||
99 | |||
15 | void init_SidebarWidget(iSidebarWidget *d) { | 100 | void init_SidebarWidget(iSidebarWidget *d) { |
16 | iWidget *w = as_Widget(d); | 101 | iWidget *w = as_Widget(d); |
17 | init_Widget(w); | 102 | init_Widget(w); |
18 | setFlags_Widget(w, resizeChildren_WidgetFlag, iTrue); | 103 | setBackgroundColor_Widget(w, none_ColorId); |
19 | d->mode = documentOutline_SidebarMode; | 104 | setFlags_Widget(w, hover_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue); |
20 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 105 | d->scrollY = 0; |
21 | w->rect.size.x = 80 * gap_UI; | 106 | d->mode = -1; |
107 | w->rect.size.x = 100 * gap_UI; | ||
108 | init_Array(&d->items, sizeof(iSidebarItem)); | ||
109 | d->hoverItem = iInvalidPos; | ||
110 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | ||
22 | setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); | 111 | setFlags_Widget(w, fixedWidth_WidgetFlag, iTrue); |
23 | iWidget *modeButtons = makeHDiv_Widget(); | 112 | const char *buttonLabels[max_SidebarMode] = { |
24 | setFlags_Widget(modeButtons, arrangeWidth_WidgetFlag | arrangeHeight_WidgetFlag, iTrue); | 113 | "\U0001f5b9 Outline", |
25 | addChild_Widget(w, iClob(modeButtons)); | 114 | "\U0001f588 Bookmarks", |
26 | addChildFlags_Widget( | 115 | "\U0001f553 History", |
27 | modeButtons, | 116 | "\U0001f464 Identities", |
28 | iClob(new_LabelWidget( | 117 | }; |
29 | "\U0001f5b9 Outline", 0, 0, format_CStr("sidebar.mode arg:%d", documentOutline_SidebarMode))), frameless_WidgetFlag); | 118 | for (int i = 0; i < max_SidebarMode; i++) { |
30 | addChildFlags_Widget( | 119 | d->modeButtons[i] = addChildFlags_Widget( |
31 | modeButtons, | 120 | w, |
32 | iClob(new_LabelWidget( | 121 | iClob(new_LabelWidget(buttonLabels[i], 0, 0, format_CStr("sidebar.mode arg:%d", i))), |
33 | "\U0001f588 Bookmarks", 0, 0, format_CStr("sidebar.mode arg:%d", bookmarks_SidebarMode))), frameless_WidgetFlag); | 122 | frameless_WidgetFlag); |
34 | addChildFlags_Widget( | 123 | } |
35 | modeButtons, | 124 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); |
36 | iClob(new_LabelWidget( | 125 | setThumb_ScrollWidget(d->scroll, 0, 0); |
37 | "\U0001f553 History", 0, 0, format_CStr("sidebar.mode arg:%d", history_SidebarMode))), frameless_WidgetFlag); | 126 | setMode_SidebarWidget(d, documentOutline_SidebarMode); |
38 | addChildFlags_Widget( | ||
39 | modeButtons, | ||
40 | iClob(new_LabelWidget( | ||
41 | "\U0001f464 Identities", 0, 0, format_CStr("sidebar.mode arg:%d", identities_SidebarMode))), frameless_WidgetFlag); | ||
42 | } | 127 | } |
43 | 128 | ||
44 | void deinit_SidebarWidget(iSidebarWidget *d) { | 129 | void deinit_SidebarWidget(iSidebarWidget *d) { |
45 | iUnused(d); | 130 | clearItems_SidebarWidget_(d); |
131 | deinit_Array(&d->items); | ||
132 | } | ||
133 | |||
134 | static iRect contentBounds_SidebarWidget_(const iSidebarWidget *d) { | ||
135 | iRect bounds = bounds_Widget(constAs_Widget(d)); | ||
136 | adjustEdges_Rect(&bounds, as_Widget(d->modeButtons[0])->rect.size.y + gap_UI, 0, 0, 0); | ||
137 | return bounds; | ||
138 | } | ||
139 | |||
140 | static size_t itemIndex_SidebarWidget_(const iSidebarWidget *d, iInt2 pos) { | ||
141 | const iRect bounds = contentBounds_SidebarWidget_(d); | ||
142 | pos.y -= top_Rect(bounds); | ||
143 | if (pos.y < 0) return iInvalidPos; | ||
144 | size_t index = pos.y / d->itemHeight; | ||
145 | if (index >= size_Array(&d->items)) return iInvalidPos; | ||
146 | return index; | ||
147 | } | ||
148 | |||
149 | static void itemClicked_SidebarWidget_(iSidebarWidget *d, size_t index) { | ||
150 | const iSidebarItem *item = constAt_Array(&d->items, index); | ||
151 | switch (d->mode) { | ||
152 | case documentOutline_SidebarMode: { | ||
153 | const iGmDocument *doc = document_DocumentWidget(document_App()); | ||
154 | const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->index); | ||
155 | postCommandf_App("document.goto loc:%p", head->text.start); | ||
156 | break; | ||
157 | } | ||
158 | } | ||
46 | } | 159 | } |
47 | 160 | ||
48 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { | 161 | static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) { |
49 | iWidget *w = as_Widget(d); | 162 | iWidget *w = as_Widget(d); |
163 | /* Handle commands. */ | ||
164 | if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | ||
165 | const char *cmd = command_UserEvent(ev); | ||
166 | if (isCommand_Widget(w, ev, "sidebar.mode")) { | ||
167 | setMode_SidebarWidget(d, arg_Command(cmd)); | ||
168 | updateItems_SidebarWidget_(d); | ||
169 | return iTrue; | ||
170 | } | ||
171 | else if (equal_Command(cmd, "tabs.changed") || equal_Command(cmd, "document.changed")) { | ||
172 | updateItems_SidebarWidget_(d); | ||
173 | } | ||
174 | } | ||
175 | if (ev->type == SDL_MOUSEMOTION) { | ||
176 | const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); | ||
177 | size_t hover = iInvalidPos; | ||
178 | if (contains_Widget(w, mouse)) { | ||
179 | hover = itemIndex_SidebarWidget_(d, mouse); | ||
180 | |||
181 | } | ||
182 | if (hover != d->hoverItem) { | ||
183 | d->hoverItem = hover; | ||
184 | refresh_Widget(w); | ||
185 | } | ||
186 | } | ||
187 | switch (processEvent_Click(&d->click, ev)) { | ||
188 | case started_ClickResult: | ||
189 | refresh_Widget(w); | ||
190 | break; | ||
191 | case finished_ClickResult: | ||
192 | if (contains_Rect(contentBounds_SidebarWidget_(d), pos_Click(&d->click)) && | ||
193 | d->hoverItem != iInvalidSize) { | ||
194 | //printf("click:%zu\n", d->hoverItem); fflush(stdout); | ||
195 | itemClicked_SidebarWidget_(d, d->hoverItem); | ||
196 | } | ||
197 | refresh_Widget(w); | ||
198 | break; | ||
199 | default: | ||
200 | break; | ||
201 | } | ||
50 | return processEvent_Widget(w, ev); | 202 | return processEvent_Widget(w, ev); |
51 | } | 203 | } |
52 | 204 | ||
53 | static void draw_SidebarWidget_(const iSidebarWidget *d) { | 205 | static void draw_SidebarWidget_(const iSidebarWidget *d) { |
54 | const iWidget *w = constAs_Widget(d); | 206 | const iWidget *w = constAs_Widget(d); |
207 | const iRect bounds = contentBounds_SidebarWidget_(d); | ||
208 | const iBool isPressing = d->click.isActive && contains_Rect(bounds, pos_Click(&d->click)); | ||
209 | iPaint p; | ||
210 | init_Paint(&p); | ||
211 | /* Draw the items. */ { | ||
212 | iInt2 pos = topLeft_Rect(bounds); | ||
213 | const int font = default_FontId; | ||
214 | iConstForEach(Array, i, &d->items) { | ||
215 | const iSidebarItem *item = i.value; | ||
216 | const iRect itemRect = { pos, init_I2(width_Rect(bounds), d->itemHeight) }; | ||
217 | const iBool isHover = (d->hoverItem == index_ArrayConstIterator(&i)); | ||
218 | if (isHover) { | ||
219 | fillRect_Paint(&p, itemRect, isPressing ? orange_ColorId : teal_ColorId); | ||
220 | } | ||
221 | setClip_Paint(&p, itemRect); | ||
222 | const int fg = isHover ? (isPressing ? black_ColorId : white_ColorId) : gray75_ColorId; | ||
223 | if (d->mode == documentOutline_SidebarMode) { | ||
224 | drawRange_Text(font, init_I2(pos.x + 3 * gap_UI + item->indent, | ||
225 | mid_Rect(itemRect).y - lineHeight_Text(font) / 2), | ||
226 | fg, range_String(&item->label)); | ||
227 | } | ||
228 | unsetClip_Paint(&p); | ||
229 | pos.y += d->itemHeight; | ||
230 | } | ||
231 | } | ||
55 | draw_Widget(w); | 232 | draw_Widget(w); |
56 | } | 233 | } |
57 | 234 | ||