Skip to content

Commit

Permalink
Add support for dragging to reorder in Fl_Tree, STR #2828 (I)
Browse files Browse the repository at this point in the history
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10275 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
  • Loading branch information
clbr committed Sep 5, 2014
1 parent d67f2e8 commit d1d203c
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 8 deletions.
3 changes: 2 additions & 1 deletion FL/Fl_Tree.H
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion FL/Fl_Tree_Prefs.H
Expand Up @@ -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
Expand Down
109 changes: 108 additions & 1 deletion src/Fl_Tree.cxx
Expand Up @@ -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());
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
15 changes: 10 additions & 5 deletions test/tree.fl
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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}
Expand Down Expand Up @@ -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 {} {
Expand Down Expand Up @@ -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);}
Expand Down

0 comments on commit d1d203c

Please sign in to comment.