summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbrecht Schlosser <albrechts.fltk@online.de>2017-08-05 16:44:49 +0000
committerAlbrecht Schlosser <albrechts.fltk@online.de>2017-08-05 16:44:49 +0000
commitf6437148793cf539c9cba35418579ab58c388ebf (patch)
tree41abbe47a83dff8c96a154e7757508b4090323c2
parent6126e8cb0ebcf5ce02603facf017c6c4d80d58ea (diff)
Add draggable-group example program.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12372 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
-rw-r--r--.gitignore4
-rw-r--r--examples/Makefile2
-rw-r--r--examples/draggable-group.cxx259
3 files changed, 264 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index bf8c087f7..58f07827a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,6 +68,7 @@
# /examples/
/examples/*.exe
/examples/clipboard
+/examples/draggable-group
/examples/fltk-versions
/examples/howto-add_fd-and-popen
/examples/howto-browser-with-icons
@@ -76,6 +77,9 @@
/examples/howto-text-over-image-button
/examples/menubar-add
/examples/nativefilechooser-simple-app
+/examples/nativefilechooser-simple
+/examples/OpenGL3-glut-test
+/examples/OpenGL3test
/examples/progress-simple
/examples/shapedwindow
/examples/table-as-container
diff --git a/examples/Makefile b/examples/Makefile
index a492f7a9a..a7b2d3c01 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -6,6 +6,7 @@ SHELL = /bin/sh
# Executables
ALL = clipboard$(EXEEXT) \
+ draggable-group$(EXEEXT) \
fltk-versions$(EXEEXT) \
howto-add_fd-and-popen$(EXEEXT) \
howto-browser-with-icons$(EXEEXT) \
@@ -41,4 +42,3 @@ clean:
$(RM) $(ALL)
$(RM) *.o
$(RM) core
-
diff --git a/examples/draggable-group.cxx b/examples/draggable-group.cxx
new file mode 100644
index 000000000..043ed5896
--- /dev/null
+++ b/examples/draggable-group.cxx
@@ -0,0 +1,259 @@
+//
+// "$Id$"
+//
+// Demonstrate deriving a class with draggable children.
+//
+// Copyright 2017 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// http://www.fltk.org/COPYING.php
+//
+// Please report all bugs and problems on the following page:
+//
+// http://www.fltk.org/str.php
+//
+
+// K Dmitrij <kdiman@...> wrote on Wed, 26 Jul 2017 19:54:12 +0000 in
+// fltk.general, thread "[fltk.general] Draggable Group for examples FLTK":
+//
+// "Here is class for drag widgets and/or their children inside its parent
+// widget. You can include it into examples of FLTK (and to adapt/modify
+// if needs)".
+//
+// Thanks to Dmitrij for this contribution.
+
+// Use `fltk-config --compile draggable-group.cxx' to build this example.
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Button.H>
+
+#include <stdio.h>
+
+/** \class DraggableGroup
+ \brief Class with draggable children, derived from Fl_Group.
+
+ Use this class if you want the user to be able to drag the children
+ of a group inside the borders of the group. DraggableGroup widgets
+ can be nested, but only direct children of the DraggableGroup widget
+ can be dragged.
+
+ Design decisions:
+ - Widgets can be dragged with the right mouse button.
+ This was chosen to avoid conflicts with other mouse button
+ events such as clicking a button.
+ - The dragged widget is raised to the top within its group for better
+ visual feedback while dragging by making it the last child of the group.
+ - The order of widgets is restored when the mouse button is released.
+ This can result in hiding the widget behind another widget, but this
+ was chosen to keep the order given by the programmer. If this
+ is not desired it can be changed easily.
+*/
+class DraggableGroup : public Fl_Group {
+
+protected:
+ int xoff, yoff; // start offsets while dragging
+ int drag_index; // index of dragged child
+ Fl_Widget *drag_widget; // dragged child widget
+
+public:
+ DraggableGroup(int X, int Y, int W, int H, const char *L = 0)
+ : Fl_Group(X, Y, W, H, L)
+ , xoff(0)
+ , yoff(0)
+ , drag_index(0)
+ , drag_widget(0) {
+ box(FL_UP_BOX);
+ }
+
+ /** Raise the dragged widget to the top (last child) of its group.
+
+ This ensures that the widget is always visible while it is dragged.
+ The original index is saved in 'drag_index' and restored when the
+ mouse button is released.
+
+ \internal
+ Since we allow only direct children of the DraggableGroup widget
+ to be dragged the argument 'q' is always a child of 'this'.
+ Note: add(q) first removes the widget from its parent group ('this')
+ and then adds it again as its last child.
+ This is the same as insert(*q, children()).
+ */
+ void top_level(Fl_Widget *q) {
+ drag_index = find(q); // save the widget's current index
+ add(q); // raise to top (make it the last child)
+ }
+
+ /** Handle FL_PUSH, FL_DRAG, and FL_RELEASE events.
+
+ All other events are handled in Fl_Group::handle().
+ Dragged widgets are limited inside the borders of their parent group.
+ */
+ virtual int handle(int e) {
+
+ switch (e) {
+
+ case FL_PUSH: {
+ if (Fl::event_button() == FL_RIGHT_MOUSE) {
+ if (Fl::belowmouse()->parent() == this) {
+ drag_widget = Fl::belowmouse();
+
+ // save pointer offsets relative to drag_widget's x/y position
+ xoff = Fl::event_x() - drag_widget->x();
+ yoff = Fl::event_y() - drag_widget->y();
+
+ top_level(drag_widget); // raise to top for visible feedback
+ redraw();
+ return 1;
+ }
+ }
+ break;
+ }
+
+ case FL_DRAG: {
+ if (!drag_widget)
+ break;
+
+ int nX = Fl::event_x() - xoff; // new x coordinate
+ int nY = Fl::event_y() - yoff; // new y coordinate
+
+ int bbx = Fl::box_dx(box()); // left and right border width
+ int bby = Fl::box_dy(box()); // top and bottom border width
+
+ // keep the widget inside its parent's borders
+
+ if (nX < x() + bbx) {
+ nX = x() + bbx;
+ } else if (nX + drag_widget->w() > x() + w() - bbx) {
+ nX = x() + w() - drag_widget->w() - bbx;
+ }
+
+ if (nY < y() + bby) {
+ nY = y() + bby;
+ } else if (nY + drag_widget->h() > y() + h() - bby) {
+ nY = y() + h() - drag_widget->h() - bby;
+ }
+
+ drag_widget->position(nX, nY); // set the new position
+ redraw();
+ return 1;
+ }
+
+ case FL_RELEASE: {
+ if (drag_widget && Fl::event_button() == FL_RIGHT_MOUSE) {
+
+ // Optional: restore the original widget order in the group.
+ // Remove the next statement (or comment it out) if not desired.
+ insert(*drag_widget, drag_index);
+
+ init_sizes(); // save widget positions for later resizing
+ drag_widget = 0;
+ redraw();
+ if (parent())
+ parent()->redraw();
+ return 1;
+ }
+ break;
+ }
+
+ default:
+ break;
+ } // switch(e)
+
+ return Fl_Group::handle(e);
+ }
+};
+
+// clear status message box after timeout
+void clear_status(void *v) {
+ Fl_Box *status = (Fl_Box *)v;
+ status->label("");
+ status->redraw();
+}
+
+// button callback: display status message for two seconds
+void button_cb(Fl_Widget *w, void *v) {
+ static char buf[128];
+ sprintf(buf, "button_cb: '%s'.\n", w->label());
+ Fl_Box *status = (Fl_Box *)v;
+ status->label(buf);
+ status->redraw();
+ Fl::remove_timeout(clear_status); // remove any pending timeout
+ Fl::add_timeout(2.0, clear_status, v);
+}
+
+int main() {
+
+ Fl_Double_Window win(500, 500, "Drag children within their parent group");
+
+ DraggableGroup area(0, 0, 500, 400, "Use the right mouse button (MB3)\nto drag objects");
+ area.align(FL_ALIGN_INSIDE);
+
+ // draggable group inside draggable area
+ DraggableGroup dobj(5, 5, 140, 140, "DraggableGroup");
+ dobj.align(FL_ALIGN_INSIDE);
+ Fl_Box b1(25, 25, 20, 20);
+ b1.color(FL_RED);
+ b1.box(FL_FLAT_BOX);
+ Fl_Box b2(105, 105, 20, 20);
+ b2.color(FL_GREEN);
+ b2.box(FL_FLAT_BOX);
+ dobj.end();
+
+ // regular group inside draggable area
+ Fl_Group dobj2(5, 280, 110, 110, "Fl_Group");
+ dobj2.box(FL_DOWN_BOX);
+ dobj2.align(FL_ALIGN_INSIDE);
+ Fl_Box b3(15, 290, 20, 20);
+ b3.color(FL_BLUE);
+ b3.box(FL_FLAT_BOX);
+ Fl_Box b4(85, 360, 20, 20);
+ b4.color(FL_YELLOW);
+ b4.box(FL_FLAT_BOX);
+ dobj2.end();
+
+ // draggable group inside draggable area
+ DraggableGroup dobj3(245, 5, 150, 150, "DraggableGroup");
+ dobj3.align(FL_ALIGN_INSIDE);
+
+ // nested draggable group
+ DraggableGroup dobj4(250, 10, 50, 50);
+ Fl_Box b5(255, 15, 15, 15);
+ b5.color(FL_BLACK);
+ b5.box(FL_FLAT_BOX);
+ Fl_Box b6(275, 30, 15, 15);
+ b6.color(FL_WHITE);
+ b6.box(FL_FLAT_BOX);
+ dobj4.end();
+
+ dobj3.end();
+
+ Fl_Button button1(200, 350, 180, 40, "Fl_Button inside DraggableGroup");
+ button1.align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
+
+ area.end();
+
+ Fl_Button button2(200, 410, 180, 40, "Fl_Button outside DraggableGroup");
+ button2.align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP);
+
+ Fl_Box *status = new Fl_Box(0, 460, win.w(), 40, "Messages ...");
+ status->box(FL_DOWN_BOX);
+
+ button1.callback(button_cb, status);
+ button2.callback(button_cb, status);
+
+ win.end();
+ win.resizable(win);
+ win.show();
+
+ return Fl::run();
+}
+
+//
+// End of "$Id$".
+//