Adds compact buttons feature to create keypads.

See test/buttons for an example.
This commit is contained in:
Matthias Melcher 2023-09-03 00:09:32 +02:00
parent 502fa72eb4
commit 5e8adebac2
15 changed files with 197 additions and 30 deletions

View File

@ -51,7 +51,11 @@ Changes in FLTK 1.4.0 Released: ??? ?? 2022
- New Fl_ICO_Image class to read Windows .ico icon files.
- New classes Fl_SVG_File_Surface and Fl_EPS_File_Surface to save any FLTK
graphics to SVG or EPS files, respectively.
- Fl_Button now supports a compact flag that visually groups closely set
buttons into keypads.
- Fl_Tabs now supports close buttons for individual tabs.
- Fl_Tabs now support four different modes for handling an
overflowing number of tabs.
- Windows platform: added support for using a manifest to set the
application's level of DPI awareness (issue #309).
- class Fl_Native_File_Chooser on the X11/Wayland platform relies on external

View File

@ -79,6 +79,7 @@ class FL_EXPORT Fl_Button : public Fl_Widget {
char value_;
char oldval;
uchar down_box_;
uchar compact_;
protected:
@ -167,6 +168,14 @@ public:
/// (for backwards compatibility)
void down_color(unsigned c) {selection_color(c);}
// handle flag for compact buttons, documentation in source code
void compact(uchar v);
/// Return true if buttons are rendered as compact buttons.
/// \return 0 if compact mode is off, 1 if it is on
/// \see compact(bool)
uchar compact() { return compact_; }
};
#endif

View File

@ -25,6 +25,7 @@
#include "Fl_Button_Type.h"
#include "Fd_Snap_Action.h"
#include "file.h"
#include <FL/Fl.H>
#include <FL/Fl_Button.H>
@ -34,6 +35,9 @@
#include <FL/Fl_Repeat_Button.H>
#include <FL/Fl_Round_Button.H>
#include <stdlib.h>
// ---- Button Types --------------------------------------------------- MARK: -
@ -61,6 +65,30 @@ Fl_Widget *Fl_Button_Type::widget(int x, int y, int w, int h) {
return new Fl_Button(x, y, w, h, "Button");
}
void Fl_Button_Type::write_properties(Fd_Project_Writer &f) {
Fl_Widget_Type::write_properties(f);
Fl_Button *btn = (Fl_Button*)o;
if (btn->compact()) {
f.write_string("compact");
f.write_string("%d", btn->compact());
}
}
void Fl_Button_Type::read_property(Fd_Project_Reader &f, const char *c) {
Fl_Button *btn = (Fl_Button*)o;
if (!strcmp(c, "compact")) {
btn->compact((uchar)atol(f.read_word()));
} else {
Fl_Widget_Type::read_property(f, c);
}
}
void Fl_Button_Type::copy_properties() {
Fl_Widget_Type::copy_properties();
Fl_Button *s = (Fl_Button*)o, *d = (Fl_Button*)live_widget;
d->compact(s->compact());
}
Fl_Button_Type Fl_Button_type;

View File

@ -35,6 +35,10 @@ public:
int is_button() const FL_OVERRIDE { return 1; }
ID id() const FL_OVERRIDE { return ID_Button; }
bool is_a(ID inID) const FL_OVERRIDE { return (inID==ID_Button) ? true : super::is_a(inID); }
void write_properties(Fd_Project_Writer &f) FL_OVERRIDE;
void read_property(Fd_Project_Reader &f, const char *) FL_OVERRIDE;
void copy_properties() FL_OVERRIDE;
};
extern Fl_Button_Type Fl_Button_type;

View File

@ -1081,6 +1081,39 @@ void down_box_cb(Fl_Choice* i, void *v) {
}
}
void compact_cb(Fl_Light_Button* i, void* v) {
if (v == LOAD) {
uchar n;
if (current_widget->is_a(Fl_Type::ID_Button)) {
n = ((Fl_Button*)(current_widget->o))->compact();
i->value(n);
i->show();
} else {
i->hide();
}
} else {
int mod = 0;
uchar n = (uchar)i->value();
for (Fl_Type *o = Fl_Type::first; o; o = o->next) {
if (o->selected && o->is_a(Fl_Type::ID_Button)) {
Fl_Widget_Type* q = (Fl_Widget_Type*)o;
uchar v = ((Fl_Button*)(q->o))->compact();
if (n != v) {
if (!mod) {
mod = 1;
undo_checkpoint();
}
((Fl_Button*)(q->o))->compact(n);
q->redraw();
}
}
}
if (mod) set_modflag(1);
}
}
////////////////////////////////////////////////////////////////
Fl_Menu_Item whenmenu[] = {
@ -3039,6 +3072,7 @@ void Fl_Widget_Type::write_widget_code(Fd_Code_Writer& f) {
if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var,
boxname(b->down_box()));
if (b->value()) f.write_c("%s%s->value(1);\n", f.indent(), var);
if (b->compact()) f.write_c("%s%s->compact(%d);\n", f.indent(), var, b->compact());
} else if (is_a(Fl_Type::ID_Input_Choice)) {
Fl_Input_Choice* b = (Fl_Input_Choice*)o;
if (b->down_box()) f.write_c("%s%s->down_box(FL_%s);\n", f.indent(), var,

View File

@ -1239,18 +1239,21 @@ ped using octal notation `\\0123`. If this option is checked, Fluid will write\
{ preset_choice[0] = new Fl_Button(85, 107, 78, 20, "Application");
preset_choice[0]->type(102);
preset_choice[0]->value(1);
preset_choice[0]->compact(1);
preset_choice[0]->selection_color(FL_DARK2);
preset_choice[0]->labelsize(11);
preset_choice[0]->callback((Fl_Callback*)edit_layout_preset_cb, (void*)(0));
} // Fl_Button* preset_choice[0]
{ preset_choice[1] = new Fl_Button(163, 107, 79, 20, "Dialog");
preset_choice[1]->type(102);
preset_choice[1]->compact(1);
preset_choice[1]->selection_color(FL_DARK2);
preset_choice[1]->labelsize(11);
preset_choice[1]->callback((Fl_Callback*)edit_layout_preset_cb, (void*)(1));
} // Fl_Button* preset_choice[1]
{ preset_choice[2] = new Fl_Button(242, 107, 78, 20, "Toolbox");
preset_choice[2]->type(102);
preset_choice[2]->compact(1);
preset_choice[2]->selection_color(FL_DARK2);
preset_choice[2]->labelsize(11);
preset_choice[2]->callback((Fl_Callback*)edit_layout_preset_cb, (void*)(2));

View File

@ -107,7 +107,7 @@ decl {void scheme_cb(Fl_Scheme_Choice *, void *);} {public local
Function {make_settings_window()} {open
} {
Fl_Window settings_window {
label {FLUID Settings} open selected
label {FLUID Settings} open
xywh {423 204 340 580} type Double align 80 non_modal visible
} {
Fl_Tabs w_settings_tabs {
@ -115,7 +115,7 @@ Function {make_settings_window()} {open
xywh {10 10 320 530} selection_color 12 labelsize 11 labelcolor 255
} {
Fl_Group {} {
label General open
label General open selected
image {icons/general_64.png} compress_image 1 xywh {10 60 320 480} labelsize 11
code0 {o->image()->scale(36, 24);}
} {
@ -471,19 +471,19 @@ g_layout_list.update_dialogs();}
label Application
user_data 0 user_data_type long
callback edit_layout_preset_cb
xywh {85 107 78 20} type Radio value 1 selection_color 45 labelsize 11
xywh {85 107 78 20} type Radio value 1 selection_color 45 labelsize 11 compact 1
}
Fl_Button {preset_choice[1]} {
label Dialog
user_data 1 user_data_type long
callback edit_layout_preset_cb
xywh {163 107 79 20} type Radio selection_color 45 labelsize 11
xywh {163 107 79 20} type Radio selection_color 45 labelsize 11 compact 1
}
Fl_Button {preset_choice[2]} {
label Toolbox
user_data 2 user_data_type long
callback edit_layout_preset_cb
xywh {242 107 78 20} type Radio selection_color 45 labelsize 11
xywh {242 107 78 20} type Radio selection_color 45 labelsize 11 compact 1
}
}
Fl_Box {} {

View File

@ -803,10 +803,16 @@ sized to fit the container.");
} // Fl_Menu_Button* o
o->end();
} // Fl_Group* o
{ Fl_Box* o = new Fl_Box(95, 140, 300, 40);
{ Fl_Box* o = new Fl_Box(95, 165, 300, 40);
o->labelsize(11);
Fl_Group::current()->resizable(o);
} // Fl_Box* o
{ Fl_Light_Button* o = new Fl_Light_Button(95, 140, 90, 20, "Compact");
o->tooltip("use compact box types for closely set buttons");
o->selection_color((Fl_Color)1);
o->labelsize(11);
o->callback((Fl_Callback*)compact_cb);
} // Fl_Light_Button* o
o->end();
} // Fl_Group* o
{ Fl_Group* o = new Fl_Group(10, 30, 400, 330, "C++");

View File

@ -655,7 +655,12 @@ Use Ctrl-J for newlines.} xywh {95 285 310 20} labelfont 1 labelsize 11 textsize
} {}
}
Fl_Box {} {
xywh {95 140 300 40} labelsize 11 resizable
xywh {95 165 300 40} labelsize 11 resizable
}
Fl_Light_Button {} {
label Compact
callback compact_cb
tooltip {use compact box types for closely set buttons} xywh {95 140 90 20} selection_color 1 labelsize 11
}
}
Fl_Group {} {

View File

@ -113,6 +113,7 @@ extern void textsize_cb(Fl_Value_Input*, void*);
extern void textcolor_cb(Fl_Button*, void*);
extern Fl_Button *w_textcolor;
extern void textcolor_menu_cb(Fl_Menu_Button*, void*);
extern void compact_cb(Fl_Light_Button*, void*);
extern void subclass_cb(Fl_Input*, void*);
extern void subtype_cb(Fl_Choice*, void*);
extern void name_cb(Fl_Input*, void*);

View File

@ -1143,12 +1143,12 @@ void fl_throw_focus(Fl_Widget *o) {
// the inactive widget and all inactive parent groups.
//
// This is used to send FL_SHORTCUT events to the Fl::belowmouse() widget
// in case the target widget itself is inactive_r(). In this case the event
// in case the target widget itself is !active_r(). In this case the event
// is sent to the first active_r() parent.
//
// This prevents sending events to inactive widgets that might get the
// input focus otherwise. The search is fast and light and avoids calling
// inactive_r() multiple times.
// !active_r() multiple times.
// See STR #3216.
//
// Returns: first active_r() widget "above" the widget wi or NULL if

View File

@ -18,6 +18,7 @@
#include <FL/Fl_Button.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Window.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Radio_Button.H>
#include <FL/Fl_Toggle_Button.H>
@ -67,7 +68,29 @@ void Fl_Button::setonly() { // set this radio button on, turn others off
void Fl_Button::draw() {
if (type() == FL_HIDDEN_BUTTON) return;
Fl_Color col = value() ? selection_color() : color();
draw_box(value() ? (down_box()?down_box():fl_down(box())) : box(), col);
Fl_Boxtype bt = value() ? (down_box()?down_box():fl_down(box())) : box();
if (compact_ && parent()) {
Fl_Widget *p = parent();
int px, py, pw = p->w(), ph = p->h();
if (p->as_window()) { px = 0; py = 0; } else { px = p->x(); py = p->y(); }
fl_push_clip(x(), y(), w(), h());
draw_box(bt, px, py, pw, ph, col);
fl_pop_clip();
const int hh = 5, ww = 5;
Fl_Color divider_color = fl_gray_ramp(FL_NUM_GRAY/3);
if (!active_r())
divider_color = fl_inactive(divider_color);
if (x()+w() != px+pw) {
fl_color(divider_color);
fl_yxline(x()+w()-1, y()+hh, y()+h()-1-hh);
}
if (y()+h() != py+ph) {
fl_color(divider_color);
fl_xyline(x()+ww, y()+h()-1, x()+w()-1-ww);
}
} else {
draw_box(bt, col);
}
draw_backdrop();
if (labeltype() == FL_NORMAL_LABEL && value()) {
Fl_Color c = labelcolor();
@ -215,11 +238,14 @@ void Fl_Button::key_release_timeout(void *d)
\param[in] L widget label, default is no label
*/
Fl_Button::Fl_Button(int X, int Y, int W, int H, const char *L)
: Fl_Widget(X,Y,W,H,L) {
: Fl_Widget(X,Y,W,H,L),
shortcut_(0),
value_(0),
oldval(0),
down_box_(FL_NO_BOX),
compact_(0)
{
box(FL_UP_BOX);
down_box(FL_NO_BOX);
value_ = oldval = 0;
shortcut_ = 0;
set_flag(SHORTCUT_LABEL);
}
@ -249,3 +275,23 @@ Fl_Toggle_Button::Fl_Toggle_Button(int X,int Y,int W,int H,const char *L)
{
type(FL_TOGGLE_BUTTON);
}
/**
Decide if buttons should be rendered in compact mode.
\image html compact_buttons_gtk.png "compact button keypad using GTK+ Scheme"
\image latex compact_buttons_gtk.png "compact button keypad using GTK+ Scheme" width=4cm
\image html compact_buttons_gleam.png "compact buttons in Gleam"
\image latex compact_buttons_gleam.png "compact buttons in Gleam" width=4cm
In compact mode, the button's surrounding border is altered to visually signal
that multiple buttons are functionally linked together. To ensure the correct
rendering of buttons in compact mode, all buttons must be part of the same
group, positioned close to each other, and aligned with the edges of the
group. Any button outlines not in contact with the parent group's outline
will be displayed as separators.
\param[in] v switch compact mode on (1) or off (0)
*/
void Fl_Button::compact(uchar v) { compact_ = v; }

View File

@ -64,7 +64,7 @@ const uchar *fl_gray_ramp() {return (draw_it_active?active_ramp:inactive_ramp)-'
Gets the drawing color to be used for the background of a box.
This method is only useful inside box drawing code. It returns the
color to be used, either fl_inactive(c) if the widget is inactive_r()
color to be used, either fl_inactive(c) if the widget is !active_r()
or \p c otherwise.
*/
Fl_Color Fl::box_color(Fl_Color c) {
@ -84,7 +84,7 @@ Fl_Color Fl::box_color(Fl_Color c) {
This method is only useful inside box drawing code. Whenever a box is
drawn with one of the standard box drawing methods, a static variable
is set depending on the widget's current state - if the widget is
inactive_r() then the internal variable is false (0), otherwise it
!active_r() then the internal variable is false (0), otherwise it
is true (1). This is faster than calling Fl_Widget::active_r()
because the state is cached.

View File

@ -25,7 +25,7 @@
#include <FL/Fl_Scheme_Choice.H>
int main(int argc, char **argv) {
Fl_Window *window = new Fl_Window(320, 170);
Fl_Window *window = new Fl_Window(420, 170);
Fl_Button *b1 = new Fl_Button(10, 10, 130, 30, "Fl_Button");
b1->tooltip("Fl_Button");
Fl_Button *b2 = new Fl_Return_Button(150, 10, 160, 30, "Fl_Return_Button");
@ -39,6 +39,25 @@ int main(int argc, char **argv) {
Fl_Button *b6 = new Fl_Check_Button(150, 90, 160, 30, "Fl_Check_Button");
b6->tooltip("Fl_Check_Button");
Fl_Group *keypad = new Fl_Group(320, 10, 90, 120);
Fl_Button *kp[11];
kp[7] = new Fl_Button(320, 10, 30, 30, "7");
kp[8] = new Fl_Button(350, 10, 30, 30, "8");
kp[9] = new Fl_Button(380, 10, 30, 30, "9");
kp[4] = new Fl_Button(320, 40, 30, 30, "4");
kp[5] = new Fl_Button(350, 40, 30, 30, "5");
kp[6] = new Fl_Button(380, 40, 30, 30, "6");
kp[1] = new Fl_Button(320, 70, 30, 30, "1");
kp[2] = new Fl_Button(350, 70, 30, 30, "2");
kp[3] = new Fl_Button(380, 70, 30, 30, "3");
kp[0] = new Fl_Button(320, 100, 60, 30, "0");
kp[10] = new Fl_Button(380, 100, 30, 30, ".");
for (int i=0; i<11; i++) {
kp[i]->compact(1);
kp[i]->selection_color(FL_SELECTION_COLOR);
}
keypad->end();
// Add a scheme choice widget for easier testing. Position the widget at
// the right window border so the menu popup doesn't cover the check boxes etc.
Fl_Scheme_Choice *scheme_choice = new Fl_Scheme_Choice(180, 130, 130, 30, "Active FLTK Scheme:");

View File

@ -13,44 +13,52 @@ Function {} {open
xywh {462 303 420 369} type Double resizable visible
} {
Fl_Group the_group {
label {activate()/deactivate() called on this Fl_Group} open selected
label {activate()/deactivate() called on this Fl_Group} open
xywh {25 25 375 295} box ENGRAVED_FRAME align 17 resizable
} {
Fl_Button {} {
label button
xywh {50 50 105 25}
xywh {50 50 105 20}
}
Fl_Light_Button {} {
label {light button}
xywh {50 80 105 25} value 1 align 16
xywh {50 75 105 20} value 1 align 16
}
Fl_Group {} {open
xywh {50 100 105 20}
} {
Fl_Button {} {
label On selected
xywh {50 100 52 20} type Radio value 1 compact 1
}
Fl_Button {} {
label Off selected
xywh {102 100 53 20} type Radio compact 1
}
}
Fl_Group {} {
label {Child group} open
xywh {50 130 105 125} box DOWN_FRAME
xywh {50 150 105 105} box DOWN_FRAME
} {
Fl_Check_Button {} {
label red
xywh {54 172 97 20} type Radio down_box DIAMOND_DOWN_BOX selection_color 1 labelcolor 1
xywh {54 192 97 20} type Radio down_box DIAMOND_DOWN_BOX selection_color 1 labelcolor 1
}
Fl_Check_Button {} {
label green
xywh {54 192 97 20} type Radio down_box DIAMOND_DOWN_BOX selection_color 2 labelcolor 2
xywh {54 212 97 20} type Radio down_box DIAMOND_DOWN_BOX selection_color 2 labelcolor 2
}
Fl_Check_Button {} {
label blue
xywh {54 212 97 20} type Radio down_box DIAMOND_DOWN_BOX selection_color 4 labelcolor 4
}
Fl_Check_Button {} {
label white
xywh {54 232 97 20} type Radio down_box DIAMOND_DOWN_BOX selection_color 55 labelcolor 55
xywh {54 232 97 20} type Radio down_box DIAMOND_DOWN_BOX selection_color 4 labelcolor 4
}
Fl_Check_Button {} {
label check
xywh {54 132 97 20} down_box DOWN_BOX
xywh {54 152 97 20} down_box DOWN_BOX
}
Fl_Round_Button {} {
label round
xywh {54 152 97 20} down_box ROUND_DOWN_BOX
xywh {54 172 97 20} down_box ROUND_DOWN_BOX
}
}
Fl_Slider {} {