Add support for dragging to reorder in Fl_Tree, STR #2828 (I)

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10275 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Lauri Kasanen 2014-09-05 12:04:28 +00:00
parent d67f2e8e3e
commit d1d203ca57
4 changed files with 123 additions and 8 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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);}