Dillo v3.1.1-98-g318d1f14
Loading...
Searching...
No Matches
fltkviewport.cc
Go to the documentation of this file.
1/*
2 * Dillo Widget
3 *
4 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
5 * Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22
23#include "fltkviewport.hh"
24
25#include <FL/Fl.H>
26#include <FL/fl_draw.H>
27#include <FL/names.h>
28
29#include <stdio.h>
30#include "../lout/msg.h"
31#include "../lout/debug.hh"
32
33using namespace lout;
34using namespace lout::object;
35using namespace lout::container::typed;
36
37namespace dw {
38namespace fltk {
39
40/*
41 * Lets SHIFT+{Left,Right} go to the parent
42 */
43class CustScrollbar : public Fl_Scrollbar
44{
45public:
46 CustScrollbar(int x, int y, int w, int h) : Fl_Scrollbar(x,y,w,h) {};
47 int handle(int e) {
48 if (e == FL_SHORTCUT && Fl::event_state() == FL_SHIFT &&
49 (Fl::event_key() == FL_Left || Fl::event_key() == FL_Right))
50 return 0;
51 return Fl_Scrollbar::handle(e);
52 }
53};
54
55FltkViewport::FltkViewport (int X, int Y, int W, int H, const char *label):
56 FltkWidgetView (X, Y, W, H, label)
57{
58 DBG_OBJ_CREATE ("dw::fltk::FltkViewport");
59
60 hscrollbar = new CustScrollbar (x (), y (), 1, 1);
61 hscrollbar->type(FL_HORIZONTAL);
62 hscrollbar->callback (hscrollbarCallback, this);
63 hscrollbar->hide();
64 add (hscrollbar);
65
66 vscrollbar = new Fl_Scrollbar (x (), y(), 1, 1);
67 vscrollbar->type(FL_VERTICAL);
68 vscrollbar->callback (vscrollbarCallback, this);
69 vscrollbar->hide();
70 add (vscrollbar);
71
72 hasDragScroll = 1;
75 scrollbarPageMode = false;
76 pageOverlap = 50;
78
79 pageScrollDelay = 0.300;
80 pageScrollInterval = 0.100;
81
86
87 gadgets =
88 new container::typed::List <object::TypedPointer < Fl_Widget> >
89 (true);
90}
91
97
99{
100 int hdiff = 0, vdiff = 0;
101 int visibility = 0;
102
103 _MSG(" >>FltkViewport::adjustScrollbarsAndGadgetsAllocation\n");
104 if (hscrollbar->visible ())
105 visibility |= 1;
106 if (vscrollbar->visible ())
107 visibility |= 2;
108
109 if (gadgets->size () > 0) {
110 switch (gadgetOrientation [visibility]) {
111 case GADGET_VERTICAL:
112 hdiff = SCROLLBAR_THICKNESS;
113 vdiff = SCROLLBAR_THICKNESS * gadgets->size ();
114 break;
115
117 hdiff = SCROLLBAR_THICKNESS * gadgets->size ();
118 vdiff = SCROLLBAR_THICKNESS;
119 break;
120 }
121 } else {
122 hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
123 vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
124 }
125
126 if (scrollbarOnLeft) {
127 hscrollbar->resize(x () + hdiff, y () + h () - SCROLLBAR_THICKNESS,
128 w () - hdiff, SCROLLBAR_THICKNESS);
129 vscrollbar->resize(x (), y (),
130 SCROLLBAR_THICKNESS, h () - vdiff);
131 } else {
132 hscrollbar->resize(x (), y () + h () - SCROLLBAR_THICKNESS,
133 w () - hdiff, SCROLLBAR_THICKNESS);
134 vscrollbar->resize(x () + w () - SCROLLBAR_THICKNESS, y (),
135 SCROLLBAR_THICKNESS, h () - vdiff);
136 }
137
138 //int X = x () + w () - SCROLLBAR_THICKNESS;
139 //int Y = y () + h () - SCROLLBAR_THICKNESS;
140 for (Iterator <TypedPointer < Fl_Widget> > it = gadgets->iterator ();
141 it.hasNext (); ) {
142 Fl_Widget *widget = it.getNext()->getTypedValue ();
143 widget->resize(x (), y (), SCROLLBAR_THICKNESS, SCROLLBAR_THICKNESS);
144
145 /* FIXME: This has no effect */
146#if 0
147 switch (gadgetOrientation [visibility]) {
148 case GADGET_VERTICAL:
150 break;
151
154 break;
155 }
156#endif
157 }
158
160}
161
167
169{
170 scroll (hscrollbar->value () - scrollX, 0);
171}
172
174{
175 scroll (0, vscrollbar->value () - scrollY);
176}
177
178void FltkViewport::vscrollbarCallback (Fl_Widget *vscrollbar,void *viewportPtr)
179{
180 ((FltkViewport*)viewportPtr)->vscrollbarChanged ();
181}
182
183void FltkViewport::hscrollbarCallback (Fl_Widget *hscrollbar,void *viewportPtr)
184{
185 ((FltkViewport*)viewportPtr)->hscrollbarChanged ();
186}
187
188// ----------------------------------------------------------------------
189
190void FltkViewport::resize(int X, int Y, int W, int H)
191{
192 bool dimension_changed = W != w() || H != h();
193
194 Fl_Group::resize(X, Y, W, H);
195 if (dimension_changed) {
196 theLayout->viewportSizeChanged (this, W, H);
198 }
199}
200
201void FltkViewport::draw_area (void *data, int x, int y, int w, int h)
202{
203 FltkViewport *vp = (FltkViewport*) data;
204 fl_push_clip(x, y, w, h);
205
206 vp->FltkWidgetView::draw ();
207
208 for (Iterator <TypedPointer < Fl_Widget> > it = vp->gadgets->iterator();
209 it.hasNext (); ) {
210 Fl_Widget *widget = it.getNext()->getTypedValue ();
211 vp->draw_child (*widget);
212 }
213
214 fl_pop_clip();
215}
216
217/*
218 * Draw the viewport.
219 *
220 * + Damage flags come in different ways, draw() should cope with them all.
221 * + Damage flags are alive for visible and hidden widgets.
222 * + FL_DAMAGE_CHILD can flag scroll bars or embedded FLTK widgets.
223 */
225{
226 const int d = damage(),
227 vis_vs = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0,
228 vis_hs = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0,
229 draw = d & (FL_DAMAGE_ALL | FL_DAMAGE_EXPOSE),
230 draw_vs = vis_vs && vscrollbar->damage (),
231 draw_hs = vis_hs && hscrollbar->damage ();
232
233 _MSG("FltkViewport::draw d=%d => ", d);
234 // scrollbars
235 if (draw || draw_vs)
236 draw_child (*vscrollbar);
237 if (draw || draw_hs)
238 draw_child (*hscrollbar);
239 if (draw && vis_vs && vis_hs) {
240 fl_color(FL_BACKGROUND_COLOR);
241 if (scrollbarOnLeft) {
242 fl_rectf(x(), y()+h()-vis_hs, vis_vs, vis_hs);
243 } else {
244 fl_rectf(x()+w()-vis_vs, y()+h()-vis_hs, vis_vs, vis_hs);
245 }
246 }
247 // main area
248 if (d == FL_DAMAGE_CHILD && (draw_vs || draw_hs)) {
249 _MSG("none\n");
250 } else if (d == (FL_DAMAGE_SCROLL | FL_DAMAGE_CHILD)) {
251 int x = this->x();
252
253 if (scrollbarOnLeft)
254 x += vis_vs;
255 fl_scroll(x, y(), w() - vis_vs, h() - vis_hs,
256 -scrollDX, -scrollDY, draw_area, this);
257 _MSG("fl_scroll()\n");
258 } else {
259 int x = this->x();
260
261 if (scrollbarOnLeft)
262 x += vis_vs;
263 draw_area(this, x, y(), w() - vis_vs, h() - vis_hs);
264 _MSG("draw_area()\n");
265 }
266
267 scrollDX = 0;
268 scrollDY = 0;
269}
270
271int FltkViewport::handle (int event)
272{
273 int ret = 0;
274 _MSG("FltkViewport::handle %s\n", fl_eventnames[event]);
275
276 switch(event) {
277 case FL_KEYBOARD:
278 /* When the viewport has focus (and not one of its children), FLTK
279 * sends the event here. Returning zero tells FLTK to resend the
280 * event as SHORTCUT, which we finally route to the parent. */
281
282 /* As we don't know the exact keybindings set by the user, we ask for
283 * all of them (except for the minimum needed to keep form navigation).*/
284 if (Fl::event_key() != FL_Tab || Fl::event_ctrl())
285 return 0;
286 break;
287
288 case FL_SHORTCUT:
289 /* send it to the parent (UI) */
290 return 0;
291
292 case FL_FOCUS:
294 break;
295
296 case FL_UNFOCUS:
298 break;
299
300 case FL_PUSH:
301 if (vscrollbar->visible() && Fl::event_inside(vscrollbar)) {
302 if (scrollbarPageMode ^ (bool) Fl::event_shift()) {
303 /* Check top and bottom actions first */
304 int yclick = Fl::event_y();
305 int ytop = y() + SCROLLBAR_THICKNESS;
306 int ybottom = y() + h() - SCROLLBAR_THICKNESS;
307 if (hscrollbar->visible())
308 ybottom -= SCROLLBAR_THICKNESS;
309
310 if (yclick <= ytop) {
312 return 1;
313 } else if (yclick >= ybottom) {
315 return 1;
316 }
317
318 if (Fl::event_button() == FL_LEFT_MOUSE)
320 else if (Fl::event_button() == FL_RIGHT_MOUSE)
322 else
324
327 /* Repeat until released */
328 if (!Fl::has_timeout(repeatPageScroll, this))
329 Fl::add_timeout(pageScrollDelay, repeatPageScroll, this);
330
331 return 1;
332 }
333 }
334 if (vscrollbar->handle(event)) {
335 verScrolling = 1;
336 }
337 } else if (hscrollbar->visible() && Fl::event_inside(hscrollbar)) {
338 if (hscrollbar->handle(event))
339 horScrolling = 1;
340 } else if (FltkWidgetView::handle(event) == 0 &&
341 Fl::event_button() == FL_MIDDLE_MOUSE) {
342 if (!hasDragScroll) {
343 /* let the parent widget handle it... */
344 return 0;
345 } else {
346 /* receive FL_DRAG and FL_RELEASE */
347 dragScrolling = 1;
348 dragX = Fl::event_x();
349 dragY = Fl::event_y();
351 }
352 }
353 return 1;
354 break;
355
356 case FL_DRAG:
357 if (Fl::event_inside(this))
358 Fl::remove_timeout(selectionScroll);
359 if (dragScrolling) {
360 scroll(dragX - Fl::event_x(), dragY - Fl::event_y());
361 dragX = Fl::event_x();
362 dragY = Fl::event_y();
363 return 1;
364 } else if (verScrolling) {
365 vscrollbar->handle(event);
366 return 1;
367 } else if (horScrolling) {
368 hscrollbar->handle(event);
369 return 1;
370 } else if (!Fl::event_inside(this)) {
371 mouse_x = Fl::event_x();
372 mouse_y = Fl::event_y();
373 if (!Fl::has_timeout(selectionScroll, this))
374 Fl::add_timeout(0.025, selectionScroll, this);
375 }
376 break;
377
378 case FL_MOUSEWHEEL:
379 if ((vscrollbar->visible() && Fl::event_inside(vscrollbar)) ||
380 Fl::event_shift()) {
381 if (Fl::event_dy() > 0) {
383 return 1;
384 } else if (Fl::event_dy() < 0) {
386 return 1;
387 }
388 }
389 return (Fl::event_dx() ? hscrollbar : vscrollbar)->handle(event);
390 break;
391
392 case FL_RELEASE:
393 Fl::remove_timeout(repeatPageScroll);
394 Fl::remove_timeout(selectionScroll);
395 if (Fl::event_button() == FL_MIDDLE_MOUSE) {
397 } else if (verScrolling) {
398 ret = vscrollbar->handle(event);
399 } else if (horScrolling) {
400 ret = hscrollbar->handle(event);
401 }
403 break;
404
405 case FL_ENTER:
406 if (vscrollbar->visible() && Fl::event_inside(vscrollbar))
407 return vscrollbar->handle(event);
408 if (hscrollbar->visible() && Fl::event_inside(hscrollbar))
409 return hscrollbar->handle(event);
410 /* could be the result of, e.g., closing another window. */
411 mouse_x = Fl::event_x();
412 mouse_y = Fl::event_y();
414 break;
415
416 case FL_MOVE:
417 /* Use LEAVE in order not to be over a link, etc., anymore. */
418 if (vscrollbar->visible() && Fl::event_inside(vscrollbar)) {
419 (void)FltkWidgetView::handle(FL_LEAVE);
420 return vscrollbar->handle(event);
421 }
422 if (hscrollbar->visible() && Fl::event_inside(hscrollbar)) {
423 (void)FltkWidgetView::handle(FL_LEAVE);
424 return hscrollbar->handle(event);
425 }
426 break;
427 case FL_LEAVE:
428 mouse_x = mouse_y = -1;
429 break;
430 }
431
432 return ret ? ret : FltkWidgetView::handle (event);
433}
434
435// ----------------------------------------------------------------------
436
437void FltkViewport::setCanvasSize (int width, int ascent, int descent)
438{
439 FltkWidgetView::setCanvasSize (width, ascent, descent);
441}
442
443/*
444 * This is used to simulate mouse motion (e.g., when scrolling).
445 */
447{
448 if (!dragScrolling && mouse_x >= x() && mouse_x < x()+w() && mouse_y >= y()
449 && mouse_y < y()+h())
450 (void)theLayout->motionNotify (this,
454}
455
456/*
457 * For scrollbars, this currently sets the same step to both vertical and
458 * horizontal. It may be differentiated if necessary.
459 */
461{
462 vscrollbar->linesize(step);
463 hscrollbar->linesize(step);
464}
465
467{
468 pageOverlap = overlap;
469}
470
472{
473 scrollbarPageMode = enable;
474}
475
477{
478 return true;
479}
480
485
490
491void FltkViewport::scrollTo (int x, int y)
492{
493 int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
494 int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
495
496 x = misc::min (x, canvasWidth - w() + hdiff);
497 x = misc::max (x, 0);
498
499 y = misc::min (y, canvasHeight - h() + vdiff);
500 y = misc::max (y, 0);
501
502 if (x == scrollX && y == scrollY) {
503 return;
504 }
505
506 /* multiple calls to scroll can happen before a redraw occurs.
507 * scrollDX and scrollDY can therefore be non-zero here.
508 */
510 scrollDX += x - scrollX;
511 scrollDY += y - scrollY;
512
513 scrollX = x;
514 scrollY = y;
515
517 damage(FL_DAMAGE_SCROLL);
520}
521
522void FltkViewport::scroll (int dx, int dy)
523{
524 scrollTo (scrollX + dx, scrollY + dy);
525}
526
528{
529 int hdiff = vscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
530 int vdiff = hscrollbar->visible () ? SCROLLBAR_THICKNESS : 0;
531 if (cmd == core::SCREEN_UP_CMD) {
532 scroll (0, -h () + pageOverlap + vdiff);
533 } else if (cmd == core::SCREEN_DOWN_CMD) {
534 scroll (0, h () - pageOverlap - vdiff);
535 } else if (cmd == core::SCREEN_LEFT_CMD) {
536 scroll (-w() + pageOverlap + hdiff, 0);
537 } else if (cmd == core::SCREEN_RIGHT_CMD) {
538 scroll (w() - pageOverlap - hdiff, 0);
539 } else if (cmd == core::LINE_UP_CMD) {
540 scroll (0, -vscrollbar->linesize ());
541 } else if (cmd == core::LINE_DOWN_CMD) {
542 scroll (0, vscrollbar->linesize ());
543 } else if (cmd == core::LEFT_CMD) {
544 scroll (-hscrollbar->linesize (), 0);
545 } else if (cmd == core::RIGHT_CMD) {
546 scroll (hscrollbar->linesize (), 0);
547 } else if (cmd == core::TOP_CMD) {
548 scrollTo (scrollX, 0);
549 } else if (cmd == core::BOTTOM_CMD) {
550 scrollTo (scrollX, canvasHeight); /* gets adjusted in scrollTo () */
551 }
552}
553
554/*
555 * Scrolling in response to selection where the cursor is outside the view.
556 */
558{
559 int distance;
560 int dx = 0, dy = 0;
561
562 if ((distance = x() - mouse_x) > 0)
563 dx = -distance * hscrollbar->linesize () / 48 - 1;
564 else if ((distance = mouse_x - (x() + w())) > 0)
565 dx = distance * hscrollbar->linesize () / 48 + 1;
566 if ((distance = y() - mouse_y) > 0)
567 dy = -distance * vscrollbar->linesize () / 48 - 1;
568 else if ((distance = mouse_y - (y() + h())) > 0)
569 dy = distance * vscrollbar->linesize () / 48 + 1;
570
571 scroll (dx, dy);
572}
573
575{
576 ((FltkViewport *)data)->selectionScroll ();
577 Fl::repeat_timeout(0.025, selectionScroll, data);
578}
579
581{
583 Fl::repeat_timeout(pageScrollInterval, repeatPageScroll, this);
584}
585
587{
588 ((FltkViewport *)data)->repeatPageScroll ();
589}
590
592{
593 scrollbarOnLeft = enable ? 1 : 0;
595 damage(FL_DAMAGE_ALL);
596}
597
598void FltkViewport::setViewportSize (int width, int height,
599 int hScrollbarThickness,
600 int vScrollbarThickness)
601{
602 int adjustReq =
603 (hscrollbar->visible() ? !hScrollbarThickness : hScrollbarThickness) ||
604 (vscrollbar->visible() ? !vScrollbarThickness : vScrollbarThickness);
605
606 _MSG("FltkViewport::setViewportSize old_w,old_h=%dx%d -> w,h=%dx%d\n"
607 "\t hThick=%d hVis=%d, vThick=%d vVis=%d, adjustReq=%d\n",
608 w(),h(),width,height,
609 hScrollbarThickness,hscrollbar->visible(),
610 vScrollbarThickness,vscrollbar->visible(), adjustReq);
611
612 (hScrollbarThickness > 0) ? hscrollbar->show () : hscrollbar->hide ();
613 (vScrollbarThickness > 0) ? vscrollbar->show () : vscrollbar->hide ();
614
615 /* If no scrollbar, go to the beginning */
616 scroll(hScrollbarThickness ? 0 : -scrollX,
617 vScrollbarThickness ? 0 : -scrollY);
618
619 /* Adjust when scrollbar visibility changes */
620 if (adjustReq)
622}
623
625{
626 // scroll all child widgets except scroll bars
627 for (int i = children () - 1; i > 0; i--) {
628 Fl_Widget *widget = child (i);
629
630 if (widget == hscrollbar || widget == vscrollbar)
631 continue;
632
633 widget->position(widget->x () - dx, widget->y () - dy);
634 }
635}
636
638{
639 return X - x () + scrollX;
640}
641
643{
644 return Y - y () + scrollY;
645}
646
648{
649 return X + x () - scrollX;
650}
651
653{
654 return Y + y () - scrollY;
655}
656
657// ----------------------------------------------------------------------
658
659void FltkViewport::setGadgetOrientation (bool hscrollbarVisible,
660 bool vscrollbarVisible,
662 gadgetOrientation)
663{
664 this->gadgetOrientation[(hscrollbarVisible ? 0 : 1) |
665 (vscrollbarVisible ? 0 : 2)] = gadgetOrientation;
667}
668
669void FltkViewport::addGadget (Fl_Widget *gadget)
670{
673 gadgets->append (new TypedPointer < Fl_Widget> (gadget));
675}
676
677
678} // namespace fltk
679} // namespace dw
#define _MSG(...)
Definition bookmarks.c:45
bool motionNotify(View *view, int x, int y, ButtonState state)
This function is called by a view, to delegate a motion notify event.
Definition layout.cc:1070
void viewportSizeChanged(View *view, int width, int height)
Definition layout.cc:1322
void scrollPosChanged(View *view, int x, int y)
Definition layout.cc:1307
void setCanvasSize(int width, int ascent, int descent)
Set the canvas size.
void setCursor(core::style::Cursor cursor)
Set the cursor appearance.
core::Layout * theLayout
void scrollTo(int x, int y)
Scroll the vieport to the given position.
static void vscrollbarCallback(Fl_Widget *vscrollbar, void *viewportPtr)
static void draw_area(void *data, int x, int y, int w, int h)
int translateCanvasXToViewX(int x)
void setPageOverlap(int overlap)
void addGadget(Fl_Widget *gadget)
int translateViewXToCanvasX(int x)
int translateViewYToCanvasY(int y)
Fl_Scrollbar * hscrollbar
Fl_Scrollbar * vscrollbar
void scroll(int dx, int dy)
int getVScrollbarThickness()
Get the thickness of the vertical scrollbar, when it is visible.
void setCanvasSize(int width, int ascent, int descent)
Set the canvas size.
void setScrollbarPageMode(bool enable)
void updateCanvasWidgets(int oldScrollX, int oldScrollY)
static void hscrollbarCallback(Fl_Widget *hscrollbar, void *viewportPtr)
void adjustScrollbarsAndGadgetsAllocation()
FltkViewport(int x, int y, int w, int h, const char *label=0)
enum dw::core::ScrollCommand pageScrolling
bool usesViewport()
Return, whether this view uses a viewport.
int translateCanvasYToViewY(int y)
GadgetOrientation gadgetOrientation[4]
void setScrollStep(int step)
void setViewportSize(int width, int height, int hScrollbarThickness, int vScrollbarThickness)
Set the viewport size.
void setScrollbarOnLeft(bool enable)
void setGadgetOrientation(bool hscrollbarVisible, bool vscrollbarVisible, GadgetOrientation gadgetOrientation)
void resize(int x, int y, int w, int h)
lout::container::typed::List< lout::object::TypedPointer< Fl_Widget > > * gadgets
int getHScrollbarThickness()
Get the thickness of the horizontal scrollbar, when it is visible.
Typed version of container::untyped::Iterator.
Definition container.hh:395
#define DBG_OBJ_DELETE()
#define DBG_OBJ_CREATE(klass)
#define H(x, y, z)
ButtonState
Platform independent representation.
Definition events.hh:15
ScrollCommand
Definition types.hh:35
@ LINE_UP_CMD
Definition types.hh:36
@ BOTTOM_CMD
Definition types.hh:37
@ RIGHT_CMD
Definition types.hh:37
@ NONE_CMD
Definition types.hh:37
@ SCREEN_LEFT_CMD
Definition types.hh:35
@ LEFT_CMD
Definition types.hh:37
@ SCREEN_DOWN_CMD
Definition types.hh:35
@ LINE_DOWN_CMD
Definition types.hh:36
@ SCREEN_UP_CMD
Definition types.hh:35
@ SCREEN_RIGHT_CMD
Definition types.hh:36
@ TOP_CMD
Definition types.hh:37
Dw is in this namespace, or sub namespaces of this one.
This namespace provides thin wrappers, implemented as C++ templates, to gain type-safety.
Definition container.hh:387
T min(T a, T b)
Definition misc.hh:40
T max(T a, T b)
Definition misc.hh:41
Here, some common classes (or interfaces) are defined, to standardize the access to other classes.
Definition object.cc:30