summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthias Melcher <github@matthiasm.com>2023-01-05 13:51:30 +0100
committerGitHub <noreply@github.com>2023-01-05 13:51:30 +0100
commit8826dca1066361b474139bcc5aeed2e3a5246ed0 (patch)
tree6819629ff3f9f014269c7cee090ab20a824af6ad /src
parent4d1a508c7e4d28fd53129da79f068a275d7160bd (diff)
Add close buttons for individual tabs in Fl_Tabs (#628)
Add close buttons for Fl_Tabs Introducing callback reasons FLUID shows all FL_WHEN_... options Adding Fl_Tabs overflow types Improved test/tabs to show new features
Diffstat (limited to 'src')
-rw-r--r--src/Fl.cxx15
-rw-r--r--src/Fl_Browser_.cxx18
-rw-r--r--src/Fl_Button.cxx19
-rw-r--r--src/Fl_Color_Chooser.cxx14
-rw-r--r--src/Fl_File_Input.cxx2
-rw-r--r--src/Fl_Help_View.cxx2
-rw-r--r--src/Fl_Input.cxx2
-rw-r--r--src/Fl_Input_.cxx10
-rw-r--r--src/Fl_Input_Choice.cxx8
-rw-r--r--src/Fl_Positioner.cxx8
-rw-r--r--src/Fl_Repeat_Button.cxx4
-rw-r--r--src/Fl_Return_Button.cxx2
-rw-r--r--src/Fl_Scrollbar.cxx2
-rw-r--r--src/Fl_Spinner.cxx6
-rw-r--r--src/Fl_Tabs.cxx397
-rw-r--r--src/Fl_Text_Editor.cxx25
-rw-r--r--src/Fl_Tile.cxx8
-rw-r--r--src/Fl_Tree.cxx2
-rw-r--r--src/Fl_Valuator.cxx4
-rw-r--r--src/Fl_Value_Input.cxx2
-rw-r--r--src/Fl_Widget.cxx49
21 files changed, 457 insertions, 142 deletions
diff --git a/src/Fl.cxx b/src/Fl.cxx
index ee16805d9..e7ba9f04d 100644
--- a/src/Fl.cxx
+++ b/src/Fl.cxx
@@ -69,6 +69,7 @@ const char *Fl::e_clipboard_type = "";
void *Fl::e_clipboard_data = NULL;
Fl_Event_Dispatch Fl::e_dispatch = 0;
+Fl_Callback_Reason Fl::callback_reason_ = FL_REASON_UNKNOWN;
unsigned char Fl::options_[] = { 0, 0 };
unsigned char Fl::options_read_ = 0;
@@ -1173,6 +1174,14 @@ static int send_event(int event, Fl_Widget* to, Fl_Window* window) {
return ret;
}
+/**
+ \brief Give the reason for calling a callback.
+ \return the reason for the current callback
+ \see Fl_Widget::when(), Fl_Widget::do_callback(), Fl_Widget::callback()
+ */
+Fl_Callback_Reason Fl::callback_reason() {
+ return callback_reason_;
+}
/**
\brief Set a new event dispatch function.
@@ -1258,6 +1267,8 @@ int Fl::handle(int e, Fl_Window* window)
another dispatch function. In that case, the user dispatch function must
decide when to call Fl::handle_(int, Fl_Window*)
+ Callbacks can set \p FL_REASON_CLOSED and \p FL_REASON_CANCELLED.
+
\param e the event type (Fl::event_number() is not yet set)
\param window the window that caused this event
\return 0 if the event was not handled
@@ -1275,7 +1286,7 @@ int Fl::handle_(int e, Fl_Window* window)
case FL_CLOSE:
if ( grab() || (modal() && window != modal()) ) return 0;
- wi->do_callback();
+ wi->do_callback(FL_REASON_CLOSED);
return 1;
case FL_SHOW:
@@ -1425,7 +1436,7 @@ int Fl::handle_(int e, Fl_Window* window)
// make Escape key close windows:
if (event_key()==FL_Escape) {
wi = modal(); if (!wi) wi = window;
- wi->do_callback();
+ wi->do_callback(FL_REASON_CANCELLED);
return 1;
}
diff --git a/src/Fl_Browser_.cxx b/src/Fl_Browser_.cxx
index 11c0b40ba..29d3fada9 100644
--- a/src/Fl_Browser_.cxx
+++ b/src/Fl_Browser_.cxx
@@ -630,7 +630,7 @@ int Fl_Browser_::select(void* item, int val, int docallbacks) {
}
if (docallbacks) {
set_changed();
- do_callback();
+ do_callback(FL_REASON_CHANGED);
}
return 1;
}
@@ -733,7 +733,7 @@ int Fl_Browser_::handle(int event) {
if (wp.deleted()) return 1;
if (when() & FL_WHEN_ENTER_KEY) {
set_changed();
- do_callback();
+ do_callback(FL_REASON_CHANGED);
}
return 1;
case ' ':
@@ -807,7 +807,7 @@ J1:
if (wp.deleted()) return 1;
if (change && (when() & FL_WHEN_CHANGED)) {
set_changed();
- do_callback();
+ do_callback(FL_REASON_CHANGED);
if (wp.deleted()) return 1;
}
} else {
@@ -821,7 +821,7 @@ J1:
if (wp.deleted()) return 1;
if (change && (when() & FL_WHEN_CHANGED)) {
set_changed();
- do_callback();
+ do_callback(FL_REASON_CHANGED);
if (wp.deleted()) return 1;
}
}
@@ -859,7 +859,7 @@ J1:
if (wp.deleted()) return 1;
if (change && (when() & FL_WHEN_CHANGED)) {
set_changed();
- do_callback();
+ do_callback(FL_REASON_CHANGED);
if (wp.deleted()) return 1;
}
}
@@ -897,7 +897,7 @@ J1:
change |= change_t;
if (change_t && (when() & FL_WHEN_CHANGED)) {
set_changed();
- do_callback();
+ do_callback(FL_REASON_CHANGED);
if (wp.deleted()) return 1;
}
}
@@ -922,16 +922,16 @@ J1:
}
if (change) {
set_changed();
- if (when() & FL_WHEN_RELEASE) do_callback();
+ if (when() & FL_WHEN_RELEASE) do_callback(FL_REASON_CHANGED);
} else {
- if (when() & FL_WHEN_NOT_CHANGED) do_callback();
+ if (when() & FL_WHEN_NOT_CHANGED) do_callback(FL_REASON_RESELECTED);
}
if (wp.deleted()) return 1;
// double click calls the callback: (like Enter Key)
if (Fl::event_clicks() && (when() & FL_WHEN_ENTER_KEY)) {
set_changed();
- do_callback();
+ do_callback(FL_REASON_CHANGED);
}
return 1;
case FL_FOCUS:
diff --git a/src/Fl_Button.cxx b/src/Fl_Button.cxx
index 1651d08f5..dec92d483 100644
--- a/src/Fl_Button.cxx
+++ b/src/Fl_Button.cxx
@@ -100,12 +100,12 @@ int Fl_Button::handle(int event) {
value_ = newval;
set_changed();
redraw();
- if (when() & FL_WHEN_CHANGED) do_callback();
+ if (when() & FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
}
return 1;
case FL_RELEASE:
if (value_ == oldval) {
- if (when() & FL_WHEN_NOT_CHANGED) do_callback();
+ if (when() & FL_WHEN_NOT_CHANGED) do_callback(FL_REASON_SELECTED);
return 1;
}
set_changed();
@@ -116,11 +116,11 @@ int Fl_Button::handle(int event) {
set_changed();
if (when() & FL_WHEN_CHANGED) {
Fl_Widget_Tracker wp(this);
- do_callback();
+ do_callback(FL_REASON_CHANGED);
if (wp.deleted()) return 1;
}
}
- if (when() & FL_WHEN_RELEASE) do_callback();
+ if (when() & FL_WHEN_RELEASE) do_callback(FL_REASON_RELEASED);
return 1;
case FL_SHORTCUT:
if (!(shortcut() ?
@@ -150,16 +150,16 @@ int Fl_Button::handle(int event) {
if (type() == FL_RADIO_BUTTON) {
if (!value_) {
setonly();
- if (when() & FL_WHEN_CHANGED) do_callback();
+ if (when() & FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
}
} else if (type() == FL_TOGGLE_BUTTON) {
value(!value());
- if (when() & FL_WHEN_CHANGED) do_callback();
+ if (when() & FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
} else {
simulate_key_action();
}
if (wp.deleted()) return 1;
- if (when() & FL_WHEN_RELEASE) do_callback();
+ if (when() & FL_WHEN_RELEASE) do_callback(FL_REASON_RELEASED);
return 1;
}
/* FALLTHROUGH */
@@ -206,6 +206,11 @@ void Fl_Button::key_release_timeout(void *d)
Derived classes may handle this differently.
+ A button may reequest callbacks with \p whne() \p FL_WHEN_CHANGED,
+ \p FL_WHEN_NOT_CHANGED, and \p FL_WHEN_RELEASE, triggering the callback
+ reasons \p FL_REASON_CHANGED, \p FL_REASON_SELECTED,
+ and \p FL_REASON_DESELECTED.
+
\param[in] X, Y, W, H position and size of the widget
\param[in] L widget label, default is no label
*/
diff --git a/src/Fl_Color_Chooser.cxx b/src/Fl_Color_Chooser.cxx
index 665c72dc6..a8b7ac5c5 100644
--- a/src/Fl_Color_Chooser.cxx
+++ b/src/Fl_Color_Chooser.cxx
@@ -220,7 +220,7 @@ int Flcc_HueBox::handle(int e) {
if (fabs(H-ih) < 3*6.0/w()) H = ih;
if (fabs(S-is) < 3*1.0/h()) S = is;
if (Fl::event_state(FL_CTRL)) H = ih;
- if (c->hsv(H, S, c->value())) c->do_callback();
+ if (c->hsv(H, S, c->value())) c->do_callback(FL_REASON_DRAGGED);
} return 1;
case FL_FOCUS : /* FALLTHROUGH */
case FL_UNFOCUS :
@@ -292,7 +292,7 @@ int Flcc_HueBox::handle_key(int key) {
Xf = (double)X/(double)w1;
Yf = (double)Y/(double)h1;
tohs(Xf, Yf, H, S);
- if (c->hsv(H, S, c->value())) c->do_callback();
+ if (c->hsv(H, S, c->value())) c->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -347,7 +347,7 @@ int Flcc_ValueBox::handle(int e) {
double Yf;
Yf = 1-(Fl::event_y()-y()-Fl::box_dy(box()))/double(h()-Fl::box_dh(box()));
if (fabs(Yf-iv)<(3*1.0/h())) Yf = iv;
- if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback();
+ if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback(FL_REASON_DRAGGED);
} return 1;
case FL_FOCUS : /* FALLTHROUGH */
case FL_UNFOCUS :
@@ -418,7 +418,7 @@ int Flcc_ValueBox::handle_key(int key) {
double Yf;
Yf = 1-((double)Y/(double)h1);
- if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback();
+ if (c->hsv(c->hue(),c->saturation(),Yf)) c->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -432,7 +432,7 @@ void Fl_Color_Chooser::rgb_cb(Fl_Widget* o, void*) {
double G = c->gvalue.value();
double B = c->bvalue.value();
if (c->mode() == M_HSV) {
- if (c->hsv(R,G,B)) c->do_callback();
+ if (c->hsv(R,G,B)) c->do_callback(FL_REASON_CHANGED);
return;
}
if (c->mode() != M_RGB) {
@@ -440,7 +440,7 @@ void Fl_Color_Chooser::rgb_cb(Fl_Widget* o, void*) {
G = G/255;
B = B/255;
}
- if (c->rgb(R,G,B)) c->do_callback();
+ if (c->rgb(R,G,B)) c->do_callback(FL_REASON_CHANGED);
}
void Fl_Color_Chooser::mode_cb(Fl_Widget* o, void*) {
@@ -455,7 +455,7 @@ void Fl_Color_Chooser::mode_cb(Fl_Widget* o, void*) {
void Fl_Color_Chooser::mode(int newMode)
{
choice.value(newMode);
- choice.do_callback();
+ choice.do_callback(FL_REASON_RESELECTED);
}
// Small local helper function:
diff --git a/src/Fl_File_Input.cxx b/src/Fl_File_Input.cxx
index 75f40c98c..5aced4111 100644
--- a/src/Fl_File_Input.cxx
+++ b/src/Fl_File_Input.cxx
@@ -269,7 +269,7 @@ Fl_File_Input::handle_button(int event) // I - Event
// Then do the callbacks, if necessary...
set_changed();
- if (when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE) ) do_callback();
+ if (when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE) ) do_callback(FL_REASON_CHANGED);
}
return 1;
diff --git a/src/Fl_Help_View.cxx b/src/Fl_Help_View.cxx
index 67ce82a36..92bf3f048 100644
--- a/src/Fl_Help_View.cxx
+++ b/src/Fl_Help_View.cxx
@@ -3553,7 +3553,7 @@ Fl_Help_View::topline(int top) // I - Top line number
scrollbar_.value(topline_, h() - scrollsize, 0, size_);
- do_callback();
+ do_callback(FL_REASON_DRAGGED);
redraw();
}
diff --git a/src/Fl_Input.cxx b/src/Fl_Input.cxx
index bc94a8897..5edbc37cc 100644
--- a/src/Fl_Input.cxx
+++ b/src/Fl_Input.cxx
@@ -577,7 +577,7 @@ int Fl_Input::handle(int event) {
// For output widgets, do the callback so the app knows the user
// did something with the mouse...
- if (readonly()) do_callback();
+ if (readonly()) do_callback(FL_REASON_RELEASED);
return 1;
diff --git a/src/Fl_Input_.cxx b/src/Fl_Input_.cxx
index a8ad63ce0..88093bffe 100644
--- a/src/Fl_Input_.cxx
+++ b/src/Fl_Input_.cxx
@@ -942,7 +942,7 @@ int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
mark_ = position_ = undo_->undoat;
set_changed();
- if (when()&FL_WHEN_CHANGED) do_callback();
+ if (when()&FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -989,7 +989,7 @@ int Fl_Input_::undo() {
while (b1 > 0 && index(b1)!='\n') b1--;
minimal_update(b1);
set_changed();
- if (when()&FL_WHEN_CHANGED) do_callback();
+ if (when()&FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -1013,9 +1013,9 @@ int Fl_Input_::copy_cuts() {
/** \internal
Checks the when() field and does a callback if indicated.
*/
-void Fl_Input_::maybe_do_callback() {
+void Fl_Input_::maybe_do_callback(Fl_Callback_Reason reason) {
if (changed() || (when()&FL_WHEN_NOT_CHANGED)) {
- do_callback();
+ do_callback(reason);
}
}
@@ -1055,7 +1055,7 @@ int Fl_Input_::handletext(int event, int X, int Y, int W, int H) {
case FL_HIDE:
fl_reset_spot();
if (!readonly() && (when() & FL_WHEN_RELEASE))
- maybe_do_callback();
+ maybe_do_callback(FL_REASON_LOST_FOCUS);
return 1;
case FL_PUSH:
diff --git a/src/Fl_Input_Choice.cxx b/src/Fl_Input_Choice.cxx
index ae38cd1c6..059a1cc2d 100644
--- a/src/Fl_Input_Choice.cxx
+++ b/src/Fl_Input_Choice.cxx
@@ -205,7 +205,7 @@ void Fl_Input_Choice::menu_cb(Fl_Widget*, void *data) {
{
o->Fl_Widget::clear_changed();
if (o->when() & FL_WHEN_NOT_CHANGED)
- o->do_callback();
+ o->do_callback(FL_REASON_RESELECTED);
}
else
{
@@ -213,7 +213,7 @@ void Fl_Input_Choice::menu_cb(Fl_Widget*, void *data) {
o->inp_->set_changed();
o->Fl_Widget::set_changed();
if (o->when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE))
- o->do_callback();
+ o->do_callback(FL_REASON_CHANGED);
}
if (wp.deleted()) return;
@@ -233,11 +233,11 @@ void Fl_Input_Choice::inp_cb(Fl_Widget*, void *data) {
if (o->inp_->changed()) {
o->Fl_Widget::set_changed();
if (o->when() & (FL_WHEN_CHANGED|FL_WHEN_RELEASE))
- o->do_callback();
+ o->do_callback(FL_REASON_CHANGED);
} else {
o->Fl_Widget::clear_changed();
if (o->when() & FL_WHEN_NOT_CHANGED)
- o->do_callback();
+ o->do_callback(FL_REASON_RESELECTED);
}
if (wp.deleted()) return;
diff --git a/src/Fl_Positioner.cxx b/src/Fl_Positioner.cxx
index af86685c1..4bdcad797 100644
--- a/src/Fl_Positioner.cxx
+++ b/src/Fl_Positioner.cxx
@@ -99,8 +99,12 @@ int Fl_Positioner::handle(int event, int X, int Y, int W, int H) {
if (!(when() & FL_WHEN_CHANGED ||
(when() & FL_WHEN_RELEASE && event == FL_RELEASE))) return 1;
if (changed() || when()&FL_WHEN_NOT_CHANGED) {
- if (event == FL_RELEASE) clear_changed();
- do_callback();
+ Fl_Callback_Reason reason = changed() ? FL_REASON_CHANGED : FL_REASON_SELECTED;
+ if (event == FL_RELEASE) {
+ clear_changed();
+ reason = FL_REASON_RELEASED;
+ }
+ do_callback(reason);
}
return 1;
default:
diff --git a/src/Fl_Repeat_Button.cxx b/src/Fl_Repeat_Button.cxx
index 03dc2a997..81b8fc5dd 100644
--- a/src/Fl_Repeat_Button.cxx
+++ b/src/Fl_Repeat_Button.cxx
@@ -23,7 +23,7 @@
void Fl_Repeat_Button::repeat_callback(void *v) {
Fl_Button *b = (Fl_Button*)v;
Fl::add_timeout(REPEAT,repeat_callback,b);
- b->do_callback();
+ b->do_callback(FL_REASON_RESELECTED);
}
int Fl_Repeat_Button::handle(int event) {
@@ -43,7 +43,7 @@ int Fl_Repeat_Button::handle(int event) {
if (value(newval)) {
if (newval) {
Fl::add_timeout(INITIALREPEAT,repeat_callback,this);
- do_callback();
+ do_callback(FL_REASON_SELECTED);
} else {
Fl::remove_timeout(repeat_callback,this);
}
diff --git a/src/Fl_Return_Button.cxx b/src/Fl_Return_Button.cxx
index 1c0f5f264..33f87e70a 100644
--- a/src/Fl_Return_Button.cxx
+++ b/src/Fl_Return_Button.cxx
@@ -52,7 +52,7 @@ int Fl_Return_Button::handle(int event) {
if (event == FL_SHORTCUT &&
(Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter)) {
simulate_key_action();
- do_callback();
+ do_callback(FL_REASON_SELECTED);
return 1;
} else
return Fl_Button::handle(event);
diff --git a/src/Fl_Scrollbar.cxx b/src/Fl_Scrollbar.cxx
index 688e760b2..78dfde992 100644
--- a/src/Fl_Scrollbar.cxx
+++ b/src/Fl_Scrollbar.cxx
@@ -188,7 +188,7 @@ int Fl_Scrollbar::handle(int event) {
Fl_Slider::value(v);
value_damage();
set_changed();
- do_callback();
+ do_callback(FL_REASON_DRAGGED);
}
return 1;}
}
diff --git a/src/Fl_Spinner.cxx b/src/Fl_Spinner.cxx
index a298d189e..7bc3bd5c4 100644
--- a/src/Fl_Spinner.cxx
+++ b/src/Fl_Spinner.cxx
@@ -70,7 +70,7 @@ void Fl_Spinner::sb_cb(Fl_Widget *w, Fl_Spinner *sb) {
}
sb->set_changed();
- sb->do_callback();
+ sb->do_callback(FL_REASON_CHANGED);
}
void Fl_Spinner::update() {
@@ -154,10 +154,10 @@ int Fl_Spinner::handle(int event) {
case FL_KEYDOWN:
case FL_SHORTCUT:
if (Fl::event_key() == FL_Up) {
- up_button_.do_callback();
+ up_button_.do_callback(FL_REASON_DRAGGED);
return 1;
} else if (Fl::event_key() == FL_Down) {
- down_button_.do_callback();
+ down_button_.do_callback(FL_REASON_DRAGGED);
return 1;
}
return 0;
diff --git a/src/Fl_Tabs.cxx b/src/Fl_Tabs.cxx
index 9b626456b..230b9edc4 100644
--- a/src/Fl_Tabs.cxx
+++ b/src/Fl_Tabs.cxx
@@ -1,7 +1,7 @@
//
// Tab widget for the Fast Light Tool Kit (FLTK).
//
-// Copyright 1998-2021 by Bill Spitzak and others.
+// Copyright 1998-2023 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
@@ -24,6 +24,7 @@
#include <FL/Fl_Tabs.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Tooltip.H>
+#include <FL/Fl_Menu_Item.H>
#include <stdio.h>
#include <stdlib.h>
@@ -31,6 +32,41 @@
#define BORDER 2
#define EXTRASPACE 10
#define SELECTION_BORDER 5
+#define EXTRAGAP 2
+
+
+/** Make sure that we redraw all tabs when new children are added. */
+int Fl_Tabs::on_insert(Fl_Widget* candidate, int index) {
+ redraw_tabs();
+ damage(FL_DAMAGE_EXPOSE);
+ return Fl_Group::on_insert(candidate, index);
+}
+
+/** Make sure that we redraw all tabs when children are moved. */
+int Fl_Tabs::on_move(int a, int b) {
+ redraw_tabs();
+ damage(FL_DAMAGE_EXPOSE);
+ return Fl_Group::on_move(a, b);
+}
+
+/** Make sure that we redraw all tabs when new children are removed. */
+void Fl_Tabs::on_remove(int index) {
+ redraw_tabs();
+ damage(FL_DAMAGE_EXPOSE|FL_DAMAGE_ALL);
+ if (child(index)->visible()) {
+ if (index+1<children())
+ value(child(index+1));
+ else if (index>0)
+ value(child(index-1));
+ }
+ Fl_Group::on_remove(index);
+}
+
+/** Make sure that we redraw all tabs when the widget size changes. */
+void Fl_Tabs::resize(int X, int Y, int W, int H) {
+ damage(FL_DAMAGE_EXPOSE);
+ Fl_Group::resize(X, Y, W, H);
+}
// Return the left edges of each tab (plus a fake left edge for a tab
// past the right-hand one). These positions are actually of the left
@@ -47,6 +83,7 @@ int Fl_Tabs::tab_positions() {
if (nc) {
tab_pos = (int*)malloc((nc+1)*sizeof(int));
tab_width = (int*)malloc((nc)*sizeof(int));
+ tab_flags = (int*)malloc((nc)*sizeof(int));
}
tab_count = nc;
}
@@ -73,33 +110,45 @@ int Fl_Tabs::tab_positions() {
o->labeltype(ot);
o->align(oa);
+ if (o->when() & FL_WHEN_CLOSED)
+ wt += labelsize()/2 + EXTRAGAP;
+
tab_width[i] = wt + EXTRASPACE;
tab_pos[i+1] = tab_pos[i] + tab_width[i] + BORDER;
+ tab_flags[i] = 0;
}
fl_draw_shortcut = prev_draw_shortcut;
int r = w();
if (tab_pos[i] <= r) return selected;
- // uh oh, they are too big:
- // pack them against right edge:
- tab_pos[i] = r;
- for (i = nc; i--;) {
- int l = r-tab_width[i];
- if (tab_pos[i+1] < l) l = tab_pos[i+1];
- if (tab_pos[i] <= l) break;
- tab_pos[i] = l;
- r -= EXTRASPACE;
- }
- // pack them against left edge and truncate width if they still don't fit:
- for (i = 0; i<nc; i++) {
- if (tab_pos[i] >= i*EXTRASPACE) break;
- tab_pos[i] = i*EXTRASPACE;
- int W = w()-1-EXTRASPACE*(nc-i) - tab_pos[i];
- if (tab_width[i] > W) tab_width[i] = W;
- }
- // adjust edges according to visiblity:
- for (i = nc; i > selected; i--) {
- tab_pos[i] = tab_pos[i-1] + tab_width[i-1];
+
+ if (overflow_type == OVERFLOW_COMPRESS) {
+ // uh oh, they are too big:
+ // pack them against right edge:
+ tab_pos[i] = r;
+ for (i = nc; i--;) {
+ int l = r-tab_width[i];
+ if (tab_pos[i+1] < l) l = tab_pos[i+1];
+ if (tab_pos[i] <= l) break;
+ tab_pos[i] = l;
+ tab_flags[i] |= 1;
+ r -= EXTRASPACE;
+ }
+ // pack them against left edge and truncate width if they still don't fit:
+ for (i = 0; i<nc; i++) {
+ if (tab_pos[i] >= i*EXTRASPACE) break;
+ tab_pos[i] = i*EXTRASPACE;
+ tab_flags[i] |= 1;
+ int W = w()-1-EXTRASPACE*(nc-i) - tab_pos[i];
+ if (tab_width[i] > W) tab_width[i] = W;
+ }
+ // adjust edges according to visiblity:
+ for (i = nc; i > selected; i--) {
+ tab_pos[i] = tab_pos[i-1] + tab_width[i-1];
+ }
+ if ((selected > 0) && (tab_pos[selected-1]+tab_width[selected-1]>tab_pos[selected]))
+ tab_flags[selected] |= 1;
+ tab_flags[selected] &= ~1;
}
return selected;
}
@@ -121,12 +170,15 @@ int Fl_Tabs::tab_height() {
else return (H <= 0) ? 0 : H;
}
-/**
- Return the widget of the tab the user clicked on at \p event_x / \p event_y.
- This is used for event handling (clicks) and by fluid to pick tabs.
+/** Return a pointer to the child widget with a tab at the given coordinates.
+
+ The Fl_Tabs::which() method returns a pointer to the child widget of the
+ Fl_Tabs container that corresponds to the tab at the given event coordinates.
+ If the event coordinates are outside the area of the tabs or if the Fl_Tabs
+ container has no children, the method returns NULL.
- \returns The child widget of the tab the user clicked on, or<br>
- 0 if there are no children or if the event is outside of the tabs area.
+ \param event_x, event_y event coordinates
+ \returns pointer to the selected child widget, or NULL
*/
Fl_Widget *Fl_Tabs::which(int event_x, int event_y) {
if (children() == 0) return 0;
@@ -141,7 +193,7 @@ Fl_Widget *Fl_Tabs::which(int event_x, int event_y) {
const int nc = children();
tab_positions();
for (int i=0; i<nc; i++) {
- if (event_x < x()+tab_pos[i+1]) {
+ if (event_x < x()+tab_pos[i+1]+tab_offset) {
ret = child(i);
break;
}
@@ -149,6 +201,92 @@ Fl_Widget *Fl_Tabs::which(int event_x, int event_y) {
return ret;
}
+/** Check whether the coordinates fall within the "close" button area of the tab.
+
+ The Fl_Tabs::hit_close() method checks whether the given event coordinates
+ fall within the area of the "close" button on the tab of the specified
+ child widget. This method should be called after the Fl_Tabs::which() method,
+ which updates a lookup table used to determine the width of each tab.
+
+ \param o check the tab of this widget
+ \param event_x, event_y event coordinatese
+ \return 1 if we hit the close button, and 0 otherwie
+ */
+int Fl_Tabs::hit_close(Fl_Widget *o, int event_x, int event_y) {
+ (void)event_y;
+ for (int i=0; i<children(); i++) {
+ if (child(i)==o) {
+ // never hit the "close" button on a compressed tab unless it's the active one
+ if (tab_flags[i] & 1)
+ return 0;
+ // did we hit the area of teh "x"?
+ int tab_x = tab_pos[i] + tab_offset + x();
+ return ( (event_x >= tab_x)
+ && (event_x < tab_x + (labelsize()+EXTRASPACE+EXTRAGAP)/2) );
+ }
+ }
+ return 0;
+}
+
+void Fl_Tabs::check_overflow_menu() {
+ int nc = children();
+ int H = tab_height(); if (H < 0) H = -H;
+ if (tab_pos[nc] > w()-H) {
+ has_overflow_menu = 1;
+ } else {
+ has_overflow_menu = 0;
+ }
+}
+
+void Fl_Tabs::handle_overflow_menu() {
+ int nc = children();
+ int H = tab_height(); if (H < 0) H = -H;
+ int vc; // number of visible children
+
+ // count visibel children
+ for (vc = 0; vc < nc; vc++) {
+ if (tab_pos[vc+1] > w()-H) break;
+ }
+ if (vc == nc) return; // children are visible
+
+ // create a menu with invisible children
+ int i, n = nc - vc;
+ overflow_menu = new Fl_Menu_Item[n+1];
+ memset(overflow_menu, 0, sizeof(Fl_Menu_Item)*(n+1));
+ for (i = 0; i < n; i++) {
+ overflow_menu[i].label(child(vc+i)->label());
+ overflow_menu[i].user_data(child(vc+i));
+ }
+ overflow_menu[i].label(NULL);
+
+ // show the menu and handle the selection
+ const Fl_Menu_Item *m = overflow_menu->popup(x()+w()-H, (tab_height()>0)?(y()+H):(y()+h()));
+ if (m)
+ value((Fl_Widget*)m->user_data());
+
+ // delete the menu until we need it next time
+ if (overflow_menu) {
+ delete[] overflow_menu;
+ overflow_menu = NULL;
+ }
+}
+
+void Fl_Tabs::draw_overflow_menu_button() {
+ int H = tab_height();
+ int X, Y;
+ if (H > 0) {
+ X = x() + w() - H;
+ Y = y();
+ } else {
+ H = -H;
+ X = x() + w() - H;
+ Y = y() + h() - H;
+ }
+ fl_draw_box(box(), X, Y, H, H, color());
+ Fl_Rect r(X, Y, H, H);
+ fl_draw_arrow(r, FL_ARROW_CHOICE, FL_ORIENT_NONE, fl_contrast(FL_BLACK, color()));
+}
+
void Fl_Tabs::redraw_tabs()
{
int H = tab_height();
@@ -162,31 +300,73 @@ void Fl_Tabs::redraw_tabs()
}
int Fl_Tabs::handle(int event) {
-
+ static int initial_x = 0;
+ static int initial_tab_offset = 0;
+ static int forward_motion_to_group = 0;
Fl_Widget *o;
int i;
switch (event) {
case FL_PUSH:
+ initial_x = Fl::event_x();
+ initial_tab_offset = tab_offset;
+ forward_motion_to_group = 0;
{
int H = tab_height();
if (H >= 0) {
- if (Fl::event_y() > y()+H) return Fl_Group::handle(event);
+ if (Fl::event_y() > y()+H) {
+ forward_motion_to_group = 1;
+ return Fl_Group::handle(event);
+ }
} else {
- if (Fl::event_y() < y()+h()+H) return Fl_Group::handle(event);
+ if (Fl::event_y() < y()+h()+H) {
+ forward_motion_to_group = 1;
+ return Fl_Group::handle(event);
+ }
+ H = - H;
+ }
+ if (has_overflow_menu && Fl::event_x() > x()+w()-H) {
+ handle_overflow_menu();
+ return 1;
}
}
/* FALLTHROUGH */
case FL_DRAG:
case FL_RELEASE:
+ if (forward_motion_to_group) {
+ return Fl_Group::handle(event);
+ }
o = which(Fl::event_x(), Fl::event_y());
+ if (overflow_type == OVERFLOW_DRAG) {
+ if (tab_pos[children()] < w() && tab_offset == 0) {
+ // fall through
+ } else if (!Fl::event_is_click()) {
+ tab_offset = initial_tab_offset + Fl::event_x() - initial_x;
+ if (tab_offset > 0) {
+ initial_tab_offset -= tab_offset;
+ tab_offset = 0;
+ } else {
+ int dw = tab_pos[children()] + tab_offset - w();
+ if (dw < -20) {
+ initial_tab_offset -= dw+20;
+ tab_offset -= dw+20;
+ }
+ }
+ damage(FL_DAMAGE_EXPOSE|FL_DAMAGE_SCROLL);
+ return 1;
+ }
+ }
if (event == FL_RELEASE) {
push(0);
if (o && Fl::visible_focus() && Fl::focus()!=this) {
Fl::focus(this);
redraw_tabs();
}
+ if (o && (o->when() & FL_WHEN_CLOSED) && hit_close(o, Fl::event_x(), Fl::event_y())) {
+ o->do_callback(FL_REASON_CLOSED);
+ return 1; // o may be deleted at this point
+ }
if (o && // Released on a tab and..
(value(o) || // tab changed value or..
(when()&(FL_WHEN_NOT_CHANGED)) // ..no change but WHEN_NOT_CHANGED set,
@@ -194,7 +374,7 @@ int Fl_Tabs::handle(int event) {
) {
Fl_Widget_Tracker wp(o);
set_changed();
- do_callback();
+ do_callback(FL_REASON_SELECTED);
if (wp.deleted()) return 1;
}
Fl_Tooltip::current(o);
@@ -240,16 +420,16 @@ int Fl_Tabs::handle(int event) {
if (child(i)->visible()) break;
value(child(i - 1));
set_changed();
- do_callback();
+ do_callback(FL_REASON_SELECTED);
return 1;
case FL_Right:
if (!children()) return 0;
if (child(children() - 1)->visible()) return 0;
- for (i = 0; i < children(); i ++)
+ for (i = 0; i < children()-1; i++)
if (child(i)->visible()) break;
value(child(i + 1));
set_changed();
- do_callback();
+ do_callback(FL_REASON_SELECTED);
return 1;
case FL_Down:
redraw();
@@ -264,8 +444,12 @@ int Fl_Tabs::handle(int event) {
if (c->test_shortcut(c->label())) {
char sc = !c->visible();
value(c);
- if (sc) set_changed();
- do_callback();
+ if (sc) {
+ set_changed();
+ do_callback(FL_REASON_SELECTED);
+ } else {
+ do_callback(FL_REASON_RESELECTED);
+ }
return 1;
}
}
@@ -300,10 +484,15 @@ int Fl_Tabs::push(Fl_Widget *o) {
/**
Gets the currently visible widget/tab.
- The value() is the first visible child (or the last child if none
- are visible) and this also hides any other children.
- This allows the tabs to be deleted, moved to other groups, and
- show()/hide() called without it screwing up.
+ The Fl_Tabs::value() method returns a pointer to the currently visible child
+ widget of the Fl_Tabs container. The visible child is the first child that
+ is currently being displayed, or the last child if none of the children are
+ being displayed.
+
+ If child widgets have been added, moved, or deleted, this method ensures that
+ only one tab is visible at a time.
+
+ \return a pointer to the currently visible child
*/
Fl_Widget* Fl_Tabs::value() {
Fl_Widget* v = 0;
@@ -317,12 +506,17 @@ Fl_Widget* Fl_Tabs::value() {
return v;
}
-/**
- Sets the widget to become the current visible widget/tab.
- Setting the value hides all other children, and makes this one
- visible, if it is really a child.
- \returns 1 if there was a change (new value different from previous),<BR>
- 0 if there was no change (new value already set)
+/** Sets the widget to become the current visible widget/tab.
+
+ The Fl_Tabs::value() method allows you to set a particular child widget of
+ the Fl_Tabs container to be the currently visible widget. If the specified
+ widget is a child of the Fl_Tabs container, it will be made visible and all
+ other children will be hidden. The method returns 1 if the value was changed,
+ and 0 if the specified value was already set.
+
+ \param[in] newvalue a poiner to a child widget
+ \return 1 if a different tab was chosen
+ \return 0 if there was no change (new value already set)
*/
int Fl_Tabs::value(Fl_Widget *newvalue) {
Fl_Widget*const* a = array();
@@ -344,12 +538,18 @@ enum {LEFT, RIGHT, SELECTED};
void Fl_Tabs::draw() {
Fl_Widget *v = value();
int H = tab_height();
+ int ty, th;
+ if (H >= 0) {
+ ty = y(); th = H;
+ } else {
+ ty = y() + h() + H; th = -H;
+ }
+ Fl_Color c = v ? v->color() : color();
- if (damage() & FL_DAMAGE_ALL) { // redraw the entire thing:
- Fl_Color c = v ? v->color() : color();
-
+ if (damage() & FL_DAMAGE_ALL) { // redraw the children
draw_box(box(), x(), y()+(H>=0?H:0), w(), h()-(H>=0?H:-H), c);
-
+ }
+ if (damage() & (FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) {
if (selection_color() != c) {
// Draw the top or bottom SELECTION_BORDER lines of the tab pane in the
// selection color so that the user knows which tab is selected...
@@ -358,33 +558,60 @@ void Fl_Tabs::draw() {
draw_box(box(), x(), clip_y, w(), SELECTION_BORDER, selection_color());
fl_pop_clip();
}
+ }
+ if (damage() & FL_DAMAGE_ALL) { // redraw the children
if (v) draw_child(*v);
} else { // redraw the child
if (v) update_child(*v);
}
+ if (damage() & FL_DAMAGE_EXPOSE) { // redraw the tab bar background
+ if (parent()) {
+ Fl_Widget *p = parent();
+ fl_push_clip(x(), ty, w(), th);
+ if (p->as_window())
+ fl_draw_box(p->box(), 0, 0, p->w(), p->h(), p->color());
+ else
+ fl_draw_box(p->box(), p->x(), p->y(), p->w(), p->h(), p->color());
+ fl_pop_clip();
+ } else {
+ fl_rectf(x(), ty, w(), th, color());
+ }
+ }
if (damage() & (FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) {
const int nc = children();
int selected = tab_positions();
int i;
+ if (H>0)
+ fl_push_clip(x(), ty, w(), th+BORDER);
+ else
+ fl_push_clip(x(), ty-BORDER, w(), th+BORDER);
Fl_Widget*const* a = array();
for (i=0; i<selected; i++)
draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
- tab_width[i], H, a[i], LEFT);
+ tab_width[i], H, a[i], tab_flags[i], LEFT);
for (i=nc-1; i > selected; i--)
draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
- tab_width[i], H, a[i], RIGHT);
+ tab_width[i], H, a[i], tab_flags[i], RIGHT);
if (v) {
i = selected;
draw_tab(x()+tab_pos[i], x()+tab_pos[i+1],
- tab_width[i], H, a[i], SELECTED);
+ tab_width[i], H, a[i], tab_flags[i], SELECTED);
}
+ if (overflow_type == OVERFLOW_PULLDOWN)
+ check_overflow_menu();
+ if (has_overflow_menu)
+ draw_overflow_menu_button();
+ fl_pop_clip();
}
}
-void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int what) {
+void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int flags, int what) {
+ x1 += tab_offset;
+ x2 += tab_offset;
int sel = (what == SELECTED);
int dh = Fl::box_dh(box());
int dy = Fl::box_dy(box());
+ int wc = 0; // width of "close" button if drawn, or 0
char prev_draw_shortcut = fl_draw_shortcut;
fl_draw_shortcut = 1;
@@ -414,8 +641,18 @@ void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int what) {
// Draw the label using the current color...
o->labelcolor(sel ? labelcolor() : o->labelcolor());
- o->draw_label(x1, y() + yofs, W, H - yofs, tab_align());
+ // Draw the "close" button if requested
+ if ( (o->when() & FL_WHEN_CLOSED) && !(flags & 1) ) {
+ int sz = labelsize()/2, sy = (H - sz)/2;
+ fl_draw_symbol("@3+", x1 + EXTRASPACE/2, y() + yofs/2 + sy, sz, sz, o->labelcolor());
+ wc = sz + EXTRAGAP;
+ }
+
+ // Draw the label text
+ o->draw_label(x1 + wc, y() + yofs, W - wc, H - yofs, tab_align());
+
+ // Draw the focus box
if (Fl::focus() == this && o->visible())
draw_focus(bt, x1, y(), W, H, bc);
@@ -432,8 +669,18 @@ void Fl_Tabs::draw_tab(int x1, int x2, int W, int H, Fl_Widget* o, int what) {
// Draw the label using the current color...
o->labelcolor(sel ? labelcolor() : o->labelcolor());
- o->draw_label(x1, y() + h() - H, W, H - yofs, tab_align());
+ // Draw the "close" button if requested
+ if ( (o->when() & FL_WHEN_CLOSED) && (x1+W < x2) ) {
+ int sz = labelsize()/2, sy = (H - sz)/2;
+ fl_draw_symbol("@3+", x1 + EXTRASPACE/2, y() + h() - H -yofs/2 + sy, sz, sz, o->labelcolor());
+ wc = sz + EXTRAGAP;
+ }
+
+ // Draw the label text
+ o->draw_label(x1 + wc, y() + h() - H, W - wc, H - yofs, tab_align());
+
+ // Draw the focus box
if (Fl::focus() == this && o->visible())
draw_focus(bt, x1, y() + h() - H, W, H, bc);
@@ -472,14 +719,21 @@ Fl_Tabs::Fl_Tabs(int X, int Y, int W, int H, const char *L) :
{
box(FL_THIN_UP_BOX);
push_ = 0;
+ overflow_type = OVERFLOW_COMPRESS;
+ tab_offset = 0;
tab_pos = 0;
tab_width = 0;
+ tab_flags = NULL;
tab_count = 0;
tab_align_ = FL_ALIGN_CENTER;
+ has_overflow_menu = 0;
+ overflow_menu = NULL;
}
Fl_Tabs::~Fl_Tabs() {
clear_tab_positions();
+ if (overflow_menu)
+ delete[] overflow_menu;
}
/**
@@ -548,4 +802,35 @@ void Fl_Tabs::clear_tab_positions() {
free(tab_width);
tab_width = 0;
}
+ if (tab_flags){
+ free(tab_flags);
+ tab_flags = NULL;
+ }
+}
+
+/** Set a method to handle an overflowing tab bar.
+
+ The Fl_Tabs widget allows you to specify how to handle the situation where
+ there are more tabs than can be displayed at once. The available options are:
+
+ - \c OVERFLOW_COMPRESS: Tabs will be compressed and overlaid on top of each other.
+ - \c OVERFLOW_CLIP: Only the first tabs that fit will be displayed.
+ - \c OVERFLOW_PULLDOWN: Tabs that do not fit will be placed in a pull-down menu.
+ - \c OVERFLOW_DRAG: The tab bar can be dragged horizontally to reveal additional tabs.
+
+ You can set the desired behavior using the overflow() method.
+
+ \param ov overflow type
+ */
+void Fl_Tabs::handle_overflow(int ov) {
+ overflow_type = ov;
+ tab_offset = 0;
+ has_overflow_menu = 0;
+ if (overflow_menu) {
+ delete[] overflow_menu;
+ overflow_menu = NULL;
+ }
+ damage(FL_DAMAGE_EXPOSE|FL_DAMAGE_ALL);
+ redraw();
}
+
diff --git a/src/Fl_Text_Editor.cxx b/src/Fl_Text_Editor.cxx
index 139350048..81dc2f441 100644
--- a/src/Fl_Text_Editor.cxx
+++ b/src/Fl_Text_Editor.cxx
@@ -239,7 +239,7 @@ int Fl_Text_Editor::kf_default(int c, Fl_Text_Editor* e) {
else e->overstrike(s);
e->show_insert_position();
e->set_changed();
- if (e->when()&FL_WHEN_CHANGED) e->do_callback();
+ if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -268,7 +268,7 @@ int Fl_Text_Editor::kf_backspace(int, Fl_Text_Editor* e) {
kill_selection(e);
e->show_insert_position();
e->set_changed();
- if (e->when()&FL_WHEN_CHANGED) e->do_callback();
+ if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -280,7 +280,7 @@ int Fl_Text_Editor::kf_enter(int, Fl_Text_Editor* e) {
e->insert("\n");
e->show_insert_position();
e->set_changed();
- if (e->when()&FL_WHEN_CHANGED) e->do_callback();
+ if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -544,7 +544,7 @@ int Fl_Text_Editor::kf_delete(int, Fl_Text_Editor* e) {
kill_selection(e);
e->show_insert_position();
e->set_changed();
- if (e->when()&FL_WHEN_CHANGED) e->do_callback();
+ if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -567,7 +567,7 @@ int Fl_Text_Editor::kf_cut(int c, Fl_Text_Editor* e) {
kf_copy(c, e);
kill_selection(e);
e->set_changed();
- if (e->when()&FL_WHEN_CHANGED) e->do_callback();
+ if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -580,7 +580,7 @@ int Fl_Text_Editor::kf_paste(int, Fl_Text_Editor* e) {
Fl::paste(*e, 1);
e->show_insert_position();
e->set_changed();
- if (e->when()&FL_WHEN_CHANGED) e->do_callback();
+ if (e->when()&FL_WHEN_CHANGED) e->do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -636,7 +636,7 @@ int Fl_Text_Editor::handle_key() {
}
show_insert_position();
set_changed();
- if (when()&FL_WHEN_CHANGED) do_callback();
+ if (when()&FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
return 1;
}
@@ -651,10 +651,11 @@ int Fl_Text_Editor::handle_key() {
}
/** does or does not a callback according to changed() and when() settings */
-void Fl_Text_Editor::maybe_do_callback() {
+void Fl_Text_Editor::maybe_do_callback(Fl_Callback_Reason reason) {
// printf("Fl_Text_Editor::maybe_do_callback()\n");
// printf("changed()=%d, when()=%x\n", changed(), when());
- if (changed() || (when()&FL_WHEN_NOT_CHANGED)) do_callback();
+ if (changed() || (when()&FL_WHEN_NOT_CHANGED))
+ do_callback(reason);
}
int Fl_Text_Editor::handle(int event) {
@@ -679,7 +680,7 @@ int Fl_Text_Editor::handle(int event) {
if (buffer()->selected()) redraw(); // Redraw selections...
// FALLTHROUGH
case FL_HIDE:
- if (when() & FL_WHEN_RELEASE) maybe_do_callback();
+ if (when() & FL_WHEN_RELEASE) maybe_do_callback(FL_REASON_LOST_FOCUS);
return 1;
case FL_KEYBOARD:
@@ -697,7 +698,7 @@ int Fl_Text_Editor::handle(int event) {
else overstrike(Fl::event_text());
show_insert_position();
set_changed();
- if (when()&FL_WHEN_CHANGED) do_callback();
+ if (when()&FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
return 1;
case FL_ENTER:
@@ -719,7 +720,7 @@ int Fl_Text_Editor::handle(int event) {
Fl::paste(*this, 0);
Fl::focus(this);
set_changed();
- if (when()&FL_WHEN_CHANGED) do_callback();
+ if (when()&FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
return 1;
}
break;
diff --git a/src/Fl_Tile.cxx b/src/Fl_Tile.cxx
index 9b42d5f69..0d81cb259 100644
--- a/src/Fl_Tile.cxx
+++ b/src/Fl_Tile.cxx
@@ -269,8 +269,12 @@ int Fl_Tile::handle(int event) {
} else
newy = sy;
position(sx,sy,newx,newy);
- if (event == FL_DRAG) set_changed();
- do_callback();
+ if (event == FL_DRAG) {
+ set_changed();
+ do_callback(FL_REASON_DRAGGED);
+ } else {
+ do_callback(FL_REASON_CHANGED);
+ }
return 1;}
}
diff --git a/src/Fl_Tree.cxx b/src/Fl_Tree.cxx
index 1dd7cc449..a545a321c 100644
--- a/src/Fl_Tree.cxx
+++ b/src/Fl_Tree.cxx
@@ -2571,7 +2571,7 @@ int Fl_Tree::is_hscroll_visible() const {
void Fl_Tree::do_callback_for_item(Fl_Tree_Item* item, Fl_Tree_Reason reason) {
callback_reason(reason);
callback_item(item);
- do_callback((Fl_Widget*)this, user_data());
+ do_callback((Fl_Widget*)this, user_data(), (Fl_Callback_Reason)reason);
}
/// Sets the item that was changed for this callback.
diff --git a/src/Fl_Valuator.cxx b/src/Fl_Valuator.cxx
index e7bcd2c8b..38383551e 100644
--- a/src/Fl_Valuator.cxx
+++ b/src/Fl_Valuator.cxx
@@ -101,7 +101,7 @@ void Fl_Valuator::handle_drag(double v) {
value_ = v;
value_damage();
set_changed();
- if (when() & FL_WHEN_CHANGED) do_callback();
+ if (when() & FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
}
}
/** Called after an FL_WHEN_RELEASE event is received and before the callback. */
@@ -113,7 +113,7 @@ void Fl_Valuator::handle_release() {
clear_changed();
// now do the callback only if slider in new position or always is on:
if (value_ != previous_value_ || when() & FL_WHEN_NOT_CHANGED) {
- do_callback();
+ do_callback(FL_REASON_RELEASED);
}
}
}
diff --git a/src/Fl_Value_Input.cxx b/src/Fl_Value_Input.cxx
index 8060b7c00..4139c1ac4 100644
--- a/src/Fl_Value_Input.cxx
+++ b/src/Fl_Value_Input.cxx
@@ -33,7 +33,7 @@ void Fl_Value_Input::input_cb(Fl_Widget*, void* v) {
if (nv != t.value() || t.when() & FL_WHEN_NOT_CHANGED) {
t.set_value(nv);
t.set_changed();
- if (t.when()) t.do_callback();
+ if (t.when()) t.do_callback(FL_REASON_CHANGED);
}
}
diff --git a/src/Fl_Widget.cxx b/src/Fl_Widget.cxx
index 1d1b71dc4..f71b27927 100644
--- a/src/Fl_Widget.cxx
+++ b/src/Fl_Widget.cxx
@@ -365,28 +365,33 @@ void Fl_Widget::bind_deimage(Fl_Image* img) {
/** Calls the widget callback function with arbitrary arguments.
- All overloads of do_callback() call this method.
- It does nothing if the widget's callback() is NULL.
- It clears the widget's \e changed flag \b after the callback was
- called unless the callback is the default callback. Hence it is not
- necessary to call clear_changed() after calling do_callback()
- in your own widget's handle() method.
-
- \note It is legal to delete the widget in the callback (i.e. in user code),
- but you must not access the widget in the handle() method after
- calling do_callback() if the widget was deleted in the callback.
- We recommend to use Fl_Widget_Tracker to check whether the widget
- was deleted in the callback.
-
- \param[in] widget call the callback with \p widget as the first argument
- \param[in] arg use \p arg as the user data (second) argument
-
- \see default_callback()
- \see callback()
- \see class Fl_Widget_Tracker
-*/
-
-void Fl_Widget::do_callback(Fl_Widget *widget, void *arg) {
+ All overloads of do_callback() call this method.
+ It does nothing if the widget's callback() is NULL.
+ It clears the widget's \e changed flag \b after the callback was
+ called unless the callback is the default callback. Hence it is not
+ necessary to call clear_changed() after calling do_callback()
+ in your own widget's handle() method.
+
+ A \p reason must be set for widgets if different actions can trigger
+ the same callback.
+
+ \note It is legal to delete the widget in the callback (i.e. in user code),
+ but you must not access the widget in the handle() method after
+ calling do_callback() if the widget was deleted in the callback.
+ We recommend to use Fl_Widget_Tracker to check whether the widget
+ was deleted in the callback.
+
+ \param[in] widget call the callback with \p widget as the first argument
+ \param[in] arg use \p arg as the user data (second) argument
+ \param[in] reason for calling this callback
+
+ \see default_callback()
+ \see callback()
+ \see class Fl_Widget_Tracker
+ \see Fl::callback_reason()
+ */
+void Fl_Widget::do_callback(Fl_Widget *widget, void *arg, Fl_Callback_Reason reason) {
+ Fl::callback_reason_ = reason;
if (!callback_) return;
Fl_Widget_Tracker wp(this);
callback_(widget, arg);