Add a "signals and callbacks" chapter into the first steps document

This commit is contained in:
Markus Gans 2018-12-28 07:46:28 +01:00
parent 831e9fe05e
commit 17540c9581
5 changed files with 323 additions and 10 deletions

View File

@ -1,3 +1,8 @@
2018-12-28 Markus Gans <guru.mail@muenster.de>
* Add the assignment operator (=) for FButton to set the button text
* Corrected shortening of overlong texts in the title bar of FDialog
* Add a "signals and callbacks" chapter into the first steps document
2018-12-25 Markus Gans <guru.mail@muenster.de> 2018-12-25 Markus Gans <guru.mail@muenster.de>
* Add a "event processing" chapter into the first steps document * Add a "event processing" chapter into the first steps document

View File

@ -302,3 +302,302 @@ the above program with gcc:
```cpp ```cpp
g++ -O2 -std=c++11 -lfinal timer.cpp -o timer g++ -O2 -std=c++11 -lfinal timer.cpp -o timer
``` ```
Signals and Callbacks
---------------------
The callback mechanism is essential for developing applications with
FINAL CUT. Callback routines allow the programmer to connect different
objects (which do not need to know each other). Connected objects notify
each other when an action occurs in a widget. To uniquely identify a widget
action, it uses signal strings. For example, if an `FButton` object gets
clicked by a keyboard or mouse, it sends the string "clicked". A signal
handler explicitly provided by Widget, in the form of a callback function
or a callback method, can react to such a signal.
A callback function is always structured as follows:
```cpp
void cb_function (FWidget* w, FDataPtr data)
{...}
```
The structure of a callback method is the same:
```cpp
void classname::cb_methode (FWidget* w, FDataPtr data)
{...}
```
We use the `addCallback()` method of the `FWidget` class to connect
to other widget objects.
For calling functions and static methods:
```cpp
void FWidget::addCallback ( const FString& cb_signal
, FCallback cb_handler
, FDataPtr data )
{...}
```
For calling a member method of a specific instance:
```cpp
void FWidget::addCallback ( const FString& cb_signal
, FWidget* cb_instance
, FMemberCallback cb_handler
, FDataPtr data )
{...}
```
There are two macros `F_FUNCTION_CALLBACK` and `F_METHOD_CALLBACK` to avoid
having to deal with necessary type conversions. With `delCallback()` you can
remove a connection to a signal handler or a widget. Alternatively, you can
use `delCallbacks()` to remove all existing callbacks from an object.
### Example of a callback function: ###
**File:** *callback-function.cpp*
```cpp
#include <final/final.h>
using namespace finalcut;
void cb_changeText (FWidget* w, FDataPtr data)
{
FButton& button = *(static_cast<FButton*>(w));
FLabel& label = *(static_cast<FLabel*>(data));
label.clear();
label << "The " << button.getClassName() << " was pressed";
label.redraw();
}
int main (int argc, char* argv[])
{
FApplication app(argc, argv);
FDialog dialog(&app);
dialog.setText ("A dialog with callback function");
dialog.setGeometry (25, 5, 45, 9);
FLabel label (&dialog);
label = "The button has never been pressed before";
label.setGeometry (2, 2, 41, 1);
FButton button (&dialog);
// Character follows '&' will be used as the accelerator key
button = "&Click me";
button.setGeometry (15, 5, 14, 1);
// Connect the button signal "clicked" with the callback function
button.addCallback
(
"clicked",
F_FUNCTION_CALLBACK (&cb_changeText),
&label
);
app.setMainWidget(&dialog);
dialog.show();
return app.exec();
}
```
*(Note: You can close the dialog with the mouse,
<kbd>Shift</kbd>+<kbd>F10</kbd> or <kbd>Ctrl</kbd>+<kbd>^</kbd>)*
After entering the source code in *callback-function.cpp* you can compile
the above program with gcc:
```cpp
g++ -O2 -lfinal callback-function.cpp -o callback-function
```
&nbsp;
### Example of a callback method: ###
**File:** *callback-method.cpp*
```cpp
#include <final/final.h>
using namespace finalcut;
class dialogWidget : public FDialog
{
public:
explicit dialogWidget (FWidget* parent = nullptr)
: FDialog(parent)
{
setText ("Callback method");
setGeometry (25, 5, 25, 7);
button.setGeometry (7, 3, 10, 1);
// Connect the button signal "clicked" with the callback method
button.addCallback
(
"clicked",
F_METHOD_CALLBACK (this, &FApplication::cb_exitApp),
nullptr
);
}
private:
FButton button{"&Quit", this};
};
int main (int argc, char* argv[])
{
FApplication app(argc, argv);
dialogWidget dialog(&app);
app.setMainWidget(&dialog);
dialog.show();
return app.exec();
}
```
*(Note: You can close the window with the mouse,
<kbd>Shift</kbd>+<kbd>F10</kbd> or <kbd>Ctrl</kbd>+<kbd>^</kbd>)*
After entering the source code in *callback-method.cpp* you can compile
the above program with gcc:
```cpp
g++ -O2 -std=c++11 -lfinal callback-method.cpp -o callback-method
```
&nbsp;
### Send custom signals ###
You can use the `emitCallback()` method to generate a user-defined signal.
You can connect this signal later with the method `addCallback()` to a
self-defined routine.
**File:** *emit-signal.cpp*
```cpp
#include <final/final.h>
using namespace finalcut;
class dialogWidget : public FDialog
{
public:
explicit dialogWidget (FWidget* parent = nullptr)
: FDialog(parent)
{
setGeometry (25, 5, 22, 7);
setText ("Emit signal");
label.setGeometry (8, 1, 5, 1);
label.setAlignment (fc::alignRight);
label.setForegroundColor (fc::Black);
plus.setGeometry (3, 3, 5, 1);
minus.setGeometry (13, 3, 5, 1);
plus.setNoUnderline();
minus.setNoUnderline();
// Connect the button signal "clicked" with the callback method
plus.addCallback
(
"clicked",
F_METHOD_CALLBACK (this, &dialogWidget::cb_plus)
);
minus.addCallback
(
"clicked",
F_METHOD_CALLBACK (this, &dialogWidget::cb_minus)
);
// Connect own signals
addCallback
(
"hot",
F_METHOD_CALLBACK (this, &dialogWidget::cb_set_red)
);
addCallback
(
"regular",
F_METHOD_CALLBACK (this, &dialogWidget::cb_set_black)
);
addCallback
(
"cold",
F_METHOD_CALLBACK (this, &dialogWidget::cb_set_blue)
);
}
private:
void cb_plus (FWidget*, FDataPtr)
{
if ( t < 100 )
t++;
if ( t == 30 )
emitCallback("hot");
else if ( t == 1 )
emitCallback("regular");
setTemperature();
}
void cb_minus (FWidget*, FDataPtr)
{
if ( t > -99 )
t--;
if ( t == 0 )
emitCallback("cold");
else if ( t == 29 )
emitCallback("regular");
setTemperature();
}
void cb_set_blue (FWidget*, FDataPtr)
{
label.setForegroundColor (fc::Blue);
}
void cb_set_black (FWidget*, FDataPtr)
{
label.setForegroundColor (fc::Black);
}
void cb_set_red (FWidget*, FDataPtr)
{
label.setForegroundColor (fc::Red);
}
void setTemperature()
{
label.clear();
label << t << "°C";
label.redraw();
}
int t = 20;
FLabel label{FString() << t << "°C", this};
FButton plus {"&+", this};
FButton minus {"&-", this};
};
int main (int argc, char* argv[])
{
FApplication app(argc, argv);
dialogWidget dialog(&app);
app.setMainWidget(&dialog);
dialog.show();
return app.exec();
}
```
```
*(Note: You can close the window with the mouse,
<kbd>Shift</kbd>+<kbd>F10</kbd> or <kbd>Ctrl</kbd>+<kbd>^</kbd>)*
After entering the source code in *emit-signal.cpp* you can compile
the above program with gcc:
```cpp
g++ -O2 -std=c++11 -lfinal emit-signal.cpp -o emit-signal
```

View File

@ -54,6 +54,13 @@ FButton::~FButton() // destructor
delOwnTimer(); delOwnTimer();
} }
// FButton operator
//----------------------------------------------------------------------
FButton& FButton::operator = (const FString& s)
{
setText(s);
return *this;
}
// public methods of FButton // public methods of FButton
//---------------------------------------------------------------------- //----------------------------------------------------------------------

View File

@ -1130,11 +1130,8 @@ inline void FDialog::drawZoomedButton()
void FDialog::drawTextBar() void FDialog::drawTextBar()
{ {
// Fill with spaces (left of the title) // Fill with spaces (left of the title)
std::size_t center_offset std::size_t center_offset = 0;
, width std::size_t x = 1;
, zoom_btn
, length
, x;
if ( getMaxColor() < 16 ) if ( getMaxColor() < 16 )
setBold(); setBold();
@ -1144,12 +1141,14 @@ void FDialog::drawTextBar()
else else
setColor (wc.titlebar_inactive_fg, wc.titlebar_inactive_bg); setColor (wc.titlebar_inactive_fg, wc.titlebar_inactive_bg);
width = std::size_t(getWidth()); std::size_t width = getWidth();
zoom_btn = getZoomButtonWidth(); std::size_t zoom_btn = getZoomButtonWidth();
length = tb_text.getLength(); std::size_t length = tb_text.getLength();
if ( width > length + MENU_BTN + zoom_btn )
center_offset = (width - length - MENU_BTN - zoom_btn) / 2; center_offset = (width - length - MENU_BTN - zoom_btn) / 2;
for (x = 1; x <= center_offset; x++) for ( ; x <= center_offset; x++)
print (' '); print (' ');
// Print title bar text // Print title bar text

View File

@ -80,6 +80,9 @@ class FButton : public FWidget
// Disable assignment operator (=) // Disable assignment operator (=)
FButton& operator = (const FButton&) = delete; FButton& operator = (const FButton&) = delete;
// Overloaded operator
FButton& operator = (const FString&);
// Accessors // Accessors
const char* getClassName() const; const char* getClassName() const;
FString& getText(); FString& getText();