295 lines
8.3 KiB
C++
295 lines
8.3 KiB
C++
//
|
|
// Shortcut Button code for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// 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
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// https://www.fltk.org/COPYING.php
|
|
//
|
|
// Please see the following page on how to report bugs and issues:
|
|
//
|
|
// https://www.fltk.org/bugs.php
|
|
//
|
|
|
|
#include <FL/Fl_Shortcut_Button.H>
|
|
|
|
#include <FL/Fl.H>
|
|
#include <FL/fl_draw.H>
|
|
#include <FL/fl_utf8.h>
|
|
#include "Fl_System_Driver.H"
|
|
#include "flstring.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
/** \class Fl_Shortcut_Button
|
|
A button that allows the user to type a key combination to create shortcuts.
|
|
After clicked once, the button catches the following keyboard events and
|
|
records the pressed keys and all modifiers. It draws a text representation of
|
|
the shortcut.
|
|
|
|
The backspace key deletes the current shortcut. A second click on the button
|
|
or moving focus makes the last shortcut permanent.
|
|
|
|
The Shortcut button calls the user callback after every change if
|
|
FL_WHEN_CHANGED is set, and when the button is no longer recording
|
|
shortcuts if FL_WHEN_RELEASE is set.
|
|
*/
|
|
|
|
/** Construct a shortcut button.
|
|
\param X, Y, W, H position and size of the button
|
|
\param l label text when no shortcut is set
|
|
*/
|
|
Fl_Shortcut_Button::Fl_Shortcut_Button(int X,int Y,int W,int H, const char* l)
|
|
: Fl_Button(X,Y,W,H,l),
|
|
hot_(false),
|
|
pre_hot_(false),
|
|
default_set_(false),
|
|
handle_default_button_(false),
|
|
pre_esc_(0),
|
|
default_shortcut_(0),
|
|
shortcut_value(0)
|
|
{
|
|
box(FL_DOWN_BOX);
|
|
selection_color(FL_SELECTION_COLOR);
|
|
type(FL_TOGGLE_BUTTON);
|
|
// suppress warning on unused private members. I keep these around in case
|
|
// we decide to activate this API again without changing the ABI.
|
|
(void)default_shortcut_;
|
|
(void)default_set_;
|
|
|
|
}
|
|
|
|
/**
|
|
Set the displayed shortcut.
|
|
\param[in] shortcut encoded as key and modifier
|
|
*/
|
|
void Fl_Shortcut_Button::value(Fl_Shortcut shortcut) {
|
|
shortcut_value = shortcut;
|
|
clear_changed();
|
|
redraw();
|
|
}
|
|
|
|
/**
|
|
Return the user selected shortcut.
|
|
\return shortcut encoded as key and modifier
|
|
*/
|
|
Fl_Shortcut Fl_Shortcut_Button::value() {
|
|
return shortcut_value;
|
|
}
|
|
|
|
#if 0
|
|
// Default shortcut settings are disabled until successful review of the UI
|
|
/* *
|
|
Set the default shortcut.
|
|
If set, and additional 'reverse' button apears that the user can click to
|
|
reset the shortcut to some default value (including 0).
|
|
\param[in] shortcut encoded as key and modifier
|
|
*/
|
|
void Fl_Shortcut_Button::default_value(Fl_Shortcut shortcut) {
|
|
default_shortcut_ = shortcut;
|
|
default_set_ = true;
|
|
redraw();
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
// Default shortcut settings are disabled until successful review of the UI
|
|
/* *
|
|
Return the default shortcut.
|
|
\return shortcut encoded as key and modifier
|
|
*/
|
|
Fl_Shortcut Fl_Shortcut_Button::default_value() {
|
|
return default_shortcut_;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
// Default shortcut settings are disabled until successful review of the UI
|
|
/* *
|
|
No longer show the button to reverse to a default shortcut.
|
|
*/
|
|
void Fl_Shortcut_Button::default_clear() {
|
|
default_set_ = false;
|
|
redraw();
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
Draw the textual representation of the shortcut button.
|
|
|
|
When the button can receive shortcut key events, it's "hot". A hot button
|
|
is drawn in selection color. A cold button is drawn as a regular text box
|
|
containing a human readable version of the shortcut key.
|
|
*/
|
|
void Fl_Shortcut_Button::draw() {
|
|
Fl_Color col = hot_ ? selection_color() : color();
|
|
Fl_Boxtype b = box();
|
|
if (hot_) {
|
|
if (down_box())
|
|
b = down_box();
|
|
else if ((b > FL_FLAT_BOX) && (b < FL_BORDER_BOX))
|
|
b = Fl_Boxtype(((int)b) ^ 1);
|
|
}
|
|
draw_box(b, col);
|
|
draw_backdrop();
|
|
|
|
int X = x() + Fl::box_dx(box());
|
|
int Y = y() + Fl::box_dy(box());
|
|
int W = w() - Fl::box_dw(box());
|
|
int H = h() - Fl::box_dh(box());
|
|
Fl_Color textcol = fl_contrast(labelcolor(), col);
|
|
if (!active_r())
|
|
textcol = fl_inactive(textcol);
|
|
fl_color(textcol);
|
|
fl_font(labelfont(), labelsize());
|
|
const char *text = label();
|
|
if (shortcut_value)
|
|
text = fl_shortcut_label(shortcut_value);
|
|
#if 0
|
|
if (default_set_) {
|
|
fl_draw(text, X, Y, W-H, H, align() | FL_ALIGN_INSIDE);
|
|
fl_draw_symbol("@-29undo", X+W-H, Y, H, H, textcol);
|
|
} else {
|
|
fl_draw(text, X, Y, W, H, align() | FL_ALIGN_INSIDE);
|
|
}
|
|
#else
|
|
fl_draw(text, X, Y, W, H, align() | FL_ALIGN_INSIDE);
|
|
#endif
|
|
if (Fl::focus() == this) draw_focus();
|
|
}
|
|
|
|
/**
|
|
Call the callback if the user is interested.
|
|
*/
|
|
void Fl_Shortcut_Button::do_end_hot_callback() {
|
|
if (when() & FL_WHEN_RELEASE) {
|
|
do_callback(FL_REASON_RELEASED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Handle keystrokes to catch the user's shortcut.
|
|
*/
|
|
int Fl_Shortcut_Button::handle(int e) {
|
|
static int alt_modifier_extra_handler = Fl::system_driver()->need_test_shortcut_extra();
|
|
#if 0
|
|
bool inside_default_button = false;
|
|
if (default_set_ && ( (e == FL_PUSH) || (e == FL_DRAG) || (e == FL_RELEASE) ) ) {
|
|
int X = x() + Fl::box_dx(box());
|
|
int W = w() - Fl::box_dw(box());
|
|
int H = h() - Fl::box_dh(box());
|
|
if (Fl::event_inside(this) && (Fl::event_x() > X+W-H))
|
|
inside_default_button = true;
|
|
}
|
|
if ((e == FL_PUSH) && default_set_ && inside_default_button) {
|
|
if (Fl::visible_focus() && handle(FL_FOCUS)) Fl::focus(this);
|
|
handle_default_button_ = true;
|
|
return 1;
|
|
}
|
|
if (handle_default_button_) {
|
|
if (e == FL_DRAG)
|
|
return 1;
|
|
if (e == FL_RELEASE) {
|
|
if (inside_default_button && (shortcut_value != default_shortcut_)) {
|
|
shortcut_value = default_shortcut_;
|
|
set_changed();
|
|
redraw();
|
|
if (when() & FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
|
|
clear_changed();
|
|
}
|
|
handle_default_button_ = false;
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
switch (e) {
|
|
case FL_PUSH:
|
|
if (Fl::visible_focus() && handle(FL_FOCUS)) Fl::focus(this);
|
|
pre_hot_ = hot_;
|
|
/* FALLTHROUGH */
|
|
case FL_DRAG:
|
|
case FL_RELEASE:
|
|
if (Fl::event_inside(this)) {
|
|
hot_ = !pre_hot_;
|
|
} else {
|
|
hot_ = pre_hot_;
|
|
}
|
|
if ((e == FL_RELEASE) && pre_hot_ && !hot_)
|
|
do_end_hot_callback();
|
|
redraw();
|
|
handle_default_button_ = false;
|
|
return 1;
|
|
case FL_UNFOCUS:
|
|
if (hot_) do_end_hot_callback();
|
|
hot_ = false;
|
|
handle_default_button_ = false;
|
|
/* FALLTHROUGH */
|
|
case FL_FOCUS:
|
|
redraw();
|
|
return 1;
|
|
case FL_KEYBOARD:
|
|
if (hot_) {
|
|
// Note: we can't really handle non-Latin shortcuts in the Fl_Shortcut
|
|
// type, so we don't handle them here either
|
|
// Todo: use fl_utf_tolower and fl_utf_toupper
|
|
int v = fl_utf8decode(Fl::event_text(), 0, 0);
|
|
if (alt_modifier_extra_handler && Fl::event_state(FL_ALT)) {
|
|
// MacOS returns special characters when the alt modifier is held down.
|
|
// FLTK handles shortcuts as ASCII keys, so let's convert the keystroke.
|
|
int c = Fl::event_key();
|
|
if ( (c>32) && (c<128) && (isalnum(c)) ) {
|
|
v = c;
|
|
if (Fl::event_state(FL_SHIFT)) {
|
|
v = toupper(c);
|
|
}
|
|
}
|
|
}
|
|
if ( (v > 32 && v < 0x7f) || (v > 0xa0 && v <= 0xff) ) {
|
|
if (isupper(v)) {
|
|
v = tolower(v);
|
|
v |= FL_SHIFT;
|
|
}
|
|
v = v | (Fl::event_state()&(FL_META|FL_ALT|FL_CTRL));
|
|
} else {
|
|
v = (Fl::event_state() & (FL_META|FL_ALT|FL_CTRL|FL_SHIFT)) | Fl::event_key();
|
|
if (v == FL_Escape) {
|
|
if (shortcut_value == FL_Escape) {
|
|
v = pre_esc_;
|
|
do_end_hot_callback();
|
|
hot_ = false;
|
|
} else {
|
|
pre_esc_ = shortcut_value;
|
|
}
|
|
}
|
|
if ((v == FL_BackSpace) && shortcut_value) {
|
|
v = 0;
|
|
}
|
|
}
|
|
if (v != (int)shortcut_value) {
|
|
shortcut_value = v;
|
|
set_changed();
|
|
redraw();
|
|
if (when() & FL_WHEN_CHANGED) do_callback(FL_REASON_CHANGED);
|
|
clear_changed();
|
|
}
|
|
return 1;
|
|
} else {
|
|
if ((Fl::event_key() == FL_Enter) || (strcmp(Fl::event_text(), " ") == 0)) {
|
|
hot_ = true;
|
|
redraw();
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
case FL_SHORTCUT:
|
|
if (hot_) return 1;
|
|
break;
|
|
}
|
|
return Fl_Button::handle(e);
|
|
}
|
|
|