diff --git a/FL/Fl_Tree.H b/FL/Fl_Tree.H index 99a1e5e2d..826973593 100644 --- a/FL/Fl_Tree.H +++ b/FL/Fl_Tree.H @@ -317,7 +317,8 @@ enum Fl_Tree_Reason { FL_TREE_REASON_RESELECTED, ///< an item was re-selected (e.g. double-clicked) #endif /*FLTK_ABI_VERSION*/ FL_TREE_REASON_OPENED, ///< an item was opened - FL_TREE_REASON_CLOSED ///< an item was closed + FL_TREE_REASON_CLOSED, ///< an item was closed + FL_TREE_REASON_DRAGGED ///< an item was dragged into a new place }; class FL_EXPORT Fl_Tree : public Fl_Group { diff --git a/FL/Fl_Tree_Prefs.H b/FL/Fl_Tree_Prefs.H index 1b1a7b139..1ef56ca52 100644 --- a/FL/Fl_Tree_Prefs.H +++ b/FL/Fl_Tree_Prefs.H @@ -67,8 +67,10 @@ enum Fl_Tree_Connector { enum Fl_Tree_Select { FL_TREE_SELECT_NONE=0, ///< Nothing selected when items are clicked FL_TREE_SELECT_SINGLE=1, ///< Single item selected when item is clicked (default) - FL_TREE_SELECT_MULTI=2 ///< Multiple items can be selected by clicking + FL_TREE_SELECT_MULTI=2, ///< Multiple items can be selected by clicking ///< with SHIFT, CTRL or mouse drags. + FL_TREE_SELECT_SINGLE_DRAGGABLE=3, ///< Single items may be selected, and they may be + ///< reordered by mouse drag. }; #if FLTK_ABI_VERSION >= 10301 diff --git a/src/Fl_Tree.cxx b/src/Fl_Tree.cxx index c5cb0817e..75befe164 100644 --- a/src/Fl_Tree.cxx +++ b/src/Fl_Tree.cxx @@ -328,6 +328,7 @@ int Fl_Tree::handle(int e) { case FL_TREE_SELECT_NONE: break; // ignore, let group have shot at event case FL_TREE_SELECT_SINGLE: + case FL_TREE_SELECT_SINGLE_DRAGGABLE: if ( is_ctrl ) { // CTRL-SPACE: (single mode) toggle if ( ! _item_focus->is_selected() ) { select_only(_item_focus, when()); @@ -389,6 +390,7 @@ int Fl_Tree::handle(int e) { switch ( _prefs.selectmode() ) { case FL_TREE_SELECT_NONE: case FL_TREE_SELECT_SINGLE: + case FL_TREE_SELECT_SINGLE_DRAGGABLE: break; case FL_TREE_SELECT_MULTI: // Do a 'select all' @@ -432,6 +434,7 @@ int Fl_Tree::handle(int e) { case FL_TREE_SELECT_NONE: break; case FL_TREE_SELECT_SINGLE: + case FL_TREE_SELECT_SINGLE_DRAGGABLE: case FL_TREE_SELECT_MULTI: deselect_all(); break; @@ -449,6 +452,7 @@ int Fl_Tree::handle(int e) { case FL_TREE_SELECT_NONE: break; case FL_TREE_SELECT_SINGLE: + case FL_TREE_SELECT_SINGLE_DRAGGABLE: select_only(item, when()); // select only this item (handles redraw) _lastselect = item; break; @@ -511,7 +515,8 @@ int Fl_Tree::handle(int e) { #endif if ( !item ) break; // not near item? ignore drag event ret |= 1; // acknowledge event - set_item_focus(item); // becomes new focus item + if (_prefs.selectmode() != FL_TREE_SELECT_SINGLE_DRAGGABLE) + set_item_focus(item); // becomes new focus item if (item==_lastselect) break; // same item as before? avoid reselect // Handle selection behavior @@ -522,6 +527,11 @@ int Fl_Tree::handle(int e) { select_only(item, when()); // select only this item (handles redraw) break; } + case FL_TREE_SELECT_SINGLE_DRAGGABLE: { + item = _lastselect; // Keep the source intact + redraw(); + break; + } case FL_TREE_SELECT_MULTI: { Fl_Tree_Item *from = next_visible_item(_lastselect, dir); // avoid reselecting item Fl_Tree_Item *to = item; @@ -535,6 +545,57 @@ int Fl_Tree::handle(int e) { break; } case FL_RELEASE: + if (_prefs.selectmode() == FL_TREE_SELECT_SINGLE_DRAGGABLE && + Fl::event_button() == FL_LEFT_MOUSE) { +#if FLTK_ABI_VERSION >= 10303 + Fl_Tree_Item *item = _root->find_clicked(_prefs, 1); // item we're on, vertically +#else + Fl_Tree_Item *item = _root->find_clicked(_prefs); // item we're on, vertically +#endif + + if (item && _lastselect && item != _lastselect && + Fl::event_x() >= item->label_x()) { + //printf("Would drag '%s' to '%s'\n", _lastselect->label(), item->label()); + // Are we dropping above or below the target item? + const int h = Fl::event_y() - item->y(); + const int mid = item->h() / 2; + const bool before = h < mid; + //printf("Dropping %s it\n", before ? "before" : "after"); + + // Do nothing if it would be a no-op + if ((before && prev(item) != _lastselect) || + (!before && next(item) != _lastselect)) { + Fl_Tree_Item *parent = item->parent(); + + if (parent) { + int pos = parent->find_child(item); + if (!before) + pos++; + + // Special case: trying to drop right before a folder + if (item->children() && item->is_open() && !before) { + parent = item; + pos = 0; + } + + // If we're moving inside the same parent, use the below/above methods + if (_lastselect->parent() == parent) { + if (before) { + _lastselect->move_above(item); + } else { + _lastselect->move_below(item); + } + } else { + _lastselect->move_into(parent, pos); + } + + redraw(); + do_callback_for_item(_lastselect, FL_TREE_REASON_DRAGGED); + } + } + } + redraw(); + } // End single-drag check ret |= 1; break; } @@ -750,6 +811,29 @@ void Fl_Tree::draw() { _vscroll->w(), _hscroll->h()); } + + // Draw dragging line + if (_prefs.selectmode() == FL_TREE_SELECT_SINGLE_DRAGGABLE && + Fl::pushed() == this) { + + Fl_Tree_Item *item = _root->find_clicked(_prefs, 1); // item we're on, vertically + if (item && item != _item_focus) { + // Are we dropping above or before the target item? + const int h = Fl::event_y() - item->y(); + const int mid = item->h() / 2; + const bool before = h < mid; + + fl_color(FL_BLACK); + + int tgt; + if (before) { + tgt = item->y(); + } else { + tgt = item->y() + item->h(); + } + fl_line(item->x(), tgt, item->x() + item->w(), tgt); + } + } } #else /// Standard FLTK draw() method, handles drawing the tree widget. @@ -775,6 +859,29 @@ void Fl_Tree::draw() { } Fl::add_timeout(.10, redraw_soon, (void*)this); // use timer to trigger redraw; we can't } + + // Draw dragging line + if (_prefs.selectmode() == FL_TREE_SELECT_SINGLE_DRAGGABLE && + Fl::pushed() == this) { + + Fl_Tree_Item *item = _root->find_clicked(_prefs); // item we're on, vertically + if (item && item != _item_focus) { + // Are we dropping above or before the target item? + const int h = Fl::event_y() - item->y(); + const int mid = item->h() / 2; + const bool before = h < mid; + + fl_color(FL_BLACK); + + int tgt; + if (before) { + tgt = item->y(); + } else { + tgt = item->y() + item->h(); + } + fl_line(item->x(), tgt, item->x() + item->w(), tgt); + } + } } // This method is undocumented, and has been removed in ABI 1.3.3 diff --git a/test/tree.fl b/test/tree.fl index 0b92231ab..7d1779fa0 100644 --- a/test/tree.fl +++ b/test/tree.fl @@ -51,6 +51,7 @@ Function {reason_as_name(Fl_Tree_Reason reason)} { case FL_TREE_REASON_DESELECTED: return("deselected"); case FL_TREE_REASON_OPENED: return("opened"); case FL_TREE_REASON_CLOSED: return("closed"); + case FL_TREE_REASON_DRAGGED: return("dragged"); \#if FLTK_ABI_VERSION >= 10301 case FL_TREE_REASON_RESELECTED: return("reselected"); \#endif @@ -345,7 +346,7 @@ Function {} {open } { Fl_Window window { label tree open - xywh {0 234 1045 580} type Double visible + xywh {1 234 1045 580} type Double visible } { Fl_Group tree { label Tree @@ -637,8 +638,9 @@ switch ( selectmode_chooser->value() ) { case 0: tree->selectmode(FL_TREE_SELECT_NONE); break; // None case 1: tree->selectmode(FL_TREE_SELECT_SINGLE); break; // Single case 2: tree->selectmode(FL_TREE_SELECT_MULTI); break; // Multi + case 3: tree->selectmode(FL_TREE_SELECT_SINGLE_DRAGGABLE); break; // Single draggable default: tree->selectmode(FL_TREE_SELECT_SINGLE); break; // Single -}} +}} open selected tooltip {Tests Fl_Tree::selectmode() Sets how Fl_Tree handles mouse selection of tree items. NONE -- Not selectable by keyboard/mouse @@ -659,6 +661,10 @@ Sets how Fl_Tree handles mouse selection of tree items. label Multi xywh {60 60 36 21} labelsize 12 } + MenuItem {} { + label {Single + drag} + xywh {70 70 36 21} labelsize 12 + } } Fl_Choice reselectmode_chooser { label {Item Reselect Mode} @@ -1393,7 +1399,7 @@ if ( !item) { } int onoff = rootselect2_toggle->value(); if ( onoff ) tree->select_all(item); // select /ROOT and its children -else tree->deselect_all(item); // deselect /ROOT and its children} selected +else tree->deselect_all(item); // deselect /ROOT and its children} tooltip {Toggle selection of the ROOT item and all children} xywh {914 219 95 16} selection_color 1 labelsize 9 } Fl_Box {} { @@ -1728,8 +1734,7 @@ helpwin->show();} callback {tree->scrollbar_size((int)tree_scrollbar_size_slider->value()); tree->redraw();} tooltip {Tests Fl_Tree::scrollbar_size() effects on tree clipping. -The value is normally 0, which causes Fl_Tree to use the global Fl::scrollbar_size() instead. -} xywh {835 499 180 16} type Horizontal color 46 selection_color 1 labelsize 11 align 4 textsize 9 +The value is normally 0, which causes Fl_Tree to use the global Fl::scrollbar_size() instead.} xywh {835 499 180 16} type Horizontal color 46 selection_color 1 labelsize 11 align 4 textsize 9 code0 {o->value(tree->scrollbar_size());} code1 {o->range(0.0, 30.0);} code2 {o->step(1.0);}