Add a "user event" chapter to the first steps document

This commit is contained in:
Markus Gans 2020-06-13 20:35:27 +02:00
parent 9845a022a3
commit 7749d28b92
4 changed files with 140 additions and 230 deletions

1
.gitignore vendored
View File

@ -38,6 +38,7 @@ examples/.deps/
examples/.libs/ examples/.libs/
examples/calculator examples/calculator
examples/dialog examples/dialog
examples/busy
examples/event-log examples/event-log
examples/string-operations examples/string-operations
examples/background-color examples/background-color

View File

@ -245,16 +245,16 @@ Event Processing
---------------- ----------------
Calling `FApplication::exec()` starts the FINAL CUT main event loop. Calling `FApplication::exec()` starts the FINAL CUT main event loop.
While the event loop is running, the system constantly checks whether While the event loop is running, the system checks all the time whether
an event has occurred and sends it to the application's currently focused an event has occurred and sends it to the application's currently focused
object. The events of the terminal such as keystrokes, mouse actions or object. The events of the terminal, such as keystrokes, mouse actions, or
resizing the terminal are translated into `FEvent` objects and sent it to terminal size changing, are translated into `FEvent` objects, and sent them to
the active `FObject`. It is also possible to use `FApplication::sendEvent()` the active `FObject`. It is also possible to use `FApplication::sendEvent()`
or `FApplication::queueEvent()` to send your own events to an object. or `FApplication::queueEvent()` to send a specific event to an object.
`FObject`-derived objects process incoming events by reimplementing the `FObject`-derived objects process incoming events by reimplementing the
virtual method `event()`. The `FObject` itself calls only virtual method `event()`. The `FObject` itself can only call its own events
`onTimer()` or `onUserEvent()` and ignores all other events. The `onTimer()` and `onUserEvent()` and ignores all other events. The
`FObject`-derived class `FWidget` also reimplements the `event()` method `FObject`-derived class `FWidget` also reimplements the `event()` method
to handle further events. `FWidget` calls the `FWidget::onKeyPress` method to handle further events. `FWidget` calls the `FWidget::onKeyPress` method
when you press a key, or the `FWidget::onMouseDown` method when you click when you press a key, or the `FWidget::onMouseDown` method when you click
@ -269,15 +269,15 @@ For example, the method `FEvent::type()` returns the type
`fc::MouseDown_Event` when you press down a mouse button. `fc::MouseDown_Event` when you press down a mouse button.
Some event types have data that cannot store in an `FEvent` object. Some event types have data that cannot store in an `FEvent` object.
For example, a click event of the mouse must store which button it For example, a click event of the mouse must store which button is
triggered where the mouse pointer was at that time. In classes derived triggered and where the mouse pointer was at that time. In classes derived
from `FEvent`, such as `FMouseEvent()`, we store this data. from `FEvent`, such as `FMouseEvent()`, we store this data.
Widgets get their events from the `event()` method inherited from FObject. Widgets get their events from the `event()` method inherited from FObject.
The implementation of `event()` in `FWidget` forwards the most common event The implementation of `event()` in `FWidget` forwards the most common event
types to specific event handlers such as `FMouseEvent()`, `FKeyEvent()` or types to specific event handlers such as `FMouseEvent()`, `FKeyEvent()` or
`FResizeEvent()`. There are many other event types. It is also possible to `FResizeEvent()`. There are many other event types. You can create own event
create own event types and send them to other objects. types and send them to other objects and widgets.
**The FINAL CUT event types:** **The FINAL CUT event types:**
@ -312,6 +312,12 @@ enum events
``` ```
**Using a timer event**
The following example starts a periodic timer that triggers an `FTimerEvent()`
every 100 ms. The virtual method `onTimer()` is then called each time in the
same dialog object.
**File:** *timer.cpp* **File:** *timer.cpp*
```cpp ```cpp
#include <final/final.h> #include <final/final.h>
@ -375,6 +381,119 @@ g++ -O2 -lfinal -std=c++11 timer.cpp -o timer
``` ```
**Using a user event**
You can use the FUserEvent() to create a individual event and send it to a
specific object. If you want to create more than one user event, you can
specify an identification number (0 in the example below) to identify the
different events. This number can get later with getUserId().
User events should be generated in the main event loop. For this purpose, the
class FApplication provides the virtual method processExternalUserEvent().
This method can be overwritten in a derived class and filled with user code.
The following example reads the average system load and creates a user event
when a value changes. This event sends the current values to an FLabel widget
and displays them in the terminal.
**File:** *user-event.cpp*
```cpp
#include <stdlib.h>
#include <final/final.h>
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
using LoadAvg = double[3];
using namespace finalcut;
class extendedApplication : public FApplication
{
public:
extendedApplication (const int& argc, char* argv[])
: FApplication(argc, argv)
{ }
private:
void processExternalUserEvent() override
{
if ( getMainWidget() )
{
if ( getloadavg(load_avg, 3) < 0 )
FApplication::getLog()->error("Can't get load average values");
if ( last_avg[0] != load_avg[0]
|| last_avg[1] != load_avg[1]
|| last_avg[2] != load_avg[2] )
{
FUserEvent user_event(fc::User_Event, 0);
user_event.setData (FDataPtr(&load_avg));
FApplication::sendEvent (getMainWidget(), &user_event);
}
for (std::size_t i = 0; i < 3; i++)
last_avg[i] = load_avg[i];
}
}
// Data member
LoadAvg load_avg{}, last_avg{};
};
class dialogWidget final : public FDialog
{
public:
explicit dialogWidget (FWidget* parent = nullptr)
: FDialog{"User event", parent}
{
FDialog::setGeometry (FPoint{25, 5}, FSize{40, 6});
loadavg_label.setGeometry (FPoint{2, 2}, FSize{36, 1});
}
private:
void onUserEvent (FUserEvent* ev) override
{
FDataPtr dataPtr = ev->getData();
auto& lavg = *(reinterpret_cast<LoadAvg*>(dataPtr));
std::setlocale(LC_NUMERIC, "C");
loadavg_label.clear();
loadavg_label << "Load average: " << lavg[0] << ", "
<< lavg[1] << ", "
<< lavg[2] << " ";
loadavg_label.redraw();
}
FLabel loadavg_label{this};
};
int main (int argc, char* argv[])
{
extendedApplication app(argc, argv);
dialogWidget dialog(&app);
FWidget::setMainWidget(&dialog);
dialog.show();
return app.exec();
}
```
<figure class="image">
<img src="first-steps_user-event.cpp.png" alt="user-event.cpp">
<figcaption>Figure 5. User event generation</figcaption>
</figure>
<br /><br />
*(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 *user-event.cpp* you can compile
the above program with gcc:
```cpp
g++ -O2 -lfinal -std=c++11 user-event.cpp -o user-event
```
Signals and Callbacks Signals and Callbacks
--------------------- ---------------------
@ -524,7 +643,7 @@ int main (int argc, char* argv[])
``` ```
<figure class="image"> <figure class="image">
<img src="first-steps_callback-function.cpp.png" alt="callback-function.cpp"> <img src="first-steps_callback-function.cpp.png" alt="callback-function.cpp">
<figcaption>Figure 5. Button with a callback function</figcaption> <figcaption>Figure 6. Button with a callback function</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -588,7 +707,7 @@ int main (int argc, char* argv[])
``` ```
<figure class="image"> <figure class="image">
<img src="first-steps_callback-lambda.cpp.png" alt="callback-lambda.cpp"> <img src="first-steps_callback-lambda.cpp.png" alt="callback-lambda.cpp">
<figcaption>Figure 6. Button with lambda expression callback.</figcaption> <figcaption>Figure 7. Button with lambda expression callback.</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -646,7 +765,7 @@ int main (int argc, char* argv[])
``` ```
<figure class="image"> <figure class="image">
<img src="first-steps_callback-method.cpp.png" alt="callback-method.cpp"> <img src="first-steps_callback-method.cpp.png" alt="callback-method.cpp">
<figcaption>Figure 7. Button with a callback method</figcaption> <figcaption>Figure 8. Button with a callback method</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -790,7 +909,7 @@ int main (int argc, char* argv[])
``` ```
<figure class="image"> <figure class="image">
<img src="first-steps_emit-signal.cpp.png" alt="emit-signal.cpp"> <img src="first-steps_emit-signal.cpp.png" alt="emit-signal.cpp">
<figcaption>Figure 8. Callbacks with custom signals</figcaption> <figcaption>Figure 9. Callbacks with custom signals</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -831,7 +950,7 @@ If you want to ignore padding spaces, you must force this with the
<figure class="image"> <figure class="image">
<img src="widget-coordinates.svg" alt="widget coordinates"> <img src="widget-coordinates.svg" alt="widget coordinates">
<figcaption>Figure 9. Widget coordinates</figcaption> <figcaption>Figure 10. Widget coordinates</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -881,7 +1000,7 @@ methods.
<figure class="image"> <figure class="image">
<img src="widget-lengths.svg" alt="widget lengths"> <img src="widget-lengths.svg" alt="widget lengths">
<figcaption>Figure 10. Width and height of a widget</figcaption> <figcaption>Figure 11. Width and height of a widget</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -934,7 +1053,7 @@ absolute geometry values as a `FRect` object, you can call the method
<figure class="image"> <figure class="image">
<img src="widget-geometry.svg" alt="widget geometry"> <img src="widget-geometry.svg" alt="widget geometry">
<figcaption>Figure 11. Geometry of widgets</figcaption> <figcaption>Figure 12. Geometry of widgets</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -994,7 +1113,7 @@ class dialogWidget : public FDialog
button.setGeometry (FPoint{1, 1}, FSize{12, 1}, false); button.setGeometry (FPoint{1, 1}, FSize{12, 1}, false);
input.setGeometry (FPoint{2, 3}, FSize{12, 1}, false); input.setGeometry (FPoint{2, 3}, FSize{12, 1}, false);
// Set dialog geometry and calling adjustSize() // Set dialog geometry and calling adjustSize()
setGeometry (FPoint{25, 5}), FSize{40, 12}); setGeometry (FPoint{25, 5}, FSize{40, 12});
setMinimumSize (FSize{25, 9}); setMinimumSize (FSize{25, 9});
} }
@ -1068,7 +1187,7 @@ int main (int argc, char* argv[])
``` ```
<figure class="image"> <figure class="image">
<img src="first-steps_size-adjustment.cpp.png" alt="size-adjustment.cpp"> <img src="first-steps_size-adjustment.cpp.png" alt="size-adjustment.cpp">
<figcaption>Figure 12. Dynamic layout</figcaption> <figcaption>Figure 13. Dynamic layout</figcaption>
</figure> </figure>
<br /><br /> <br /><br />
@ -1198,7 +1317,7 @@ int main (int argc, char* argv[])
``` ```
<figure class="image"> <figure class="image">
<img src="first-steps_scrollview.cpp.png" alt="scrollview.cpp"> <img src="first-steps_scrollview.cpp.png" alt="scrollview.cpp">
<figcaption>Figure 13. Dialog with a scrolling viewport</figcaption> <figcaption>Figure 14. Dialog with a scrolling viewport</figcaption>
</figure> </figure>
<br /><br /> <br /><br />

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

View File

@ -1,210 +0,0 @@
#! /bin/bash
# busy - temporary wrapper script for .libs/busy
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2
#
# The busy program cannot be directly executed until all the libtool
# libraries that it depends on are installed.
#
# This wrapper script should never be moved out of the build directory.
# If it is, it will not operate correctly.
# Sed substitution that helps us do robust quoting. It backslashifies
# metacharacters that are still active within double-quoted strings.
sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
# Be Bourne compatible
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
emulate sh
NULLCMD=:
# Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
fi
BIN_SH=xpg4; export BIN_SH # for Tru64
DUALCASE=1; export DUALCASE # for MKS sh
# The HP-UX ksh and POSIX shell print the target directory to stdout
# if CDPATH is set.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
relink_command=""
# This environment variable determines our operation mode.
if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
# install mode needs the following variables:
generated_by_libtool_version='2.4.6'
notinst_deplibs=' /usr/local/src/MyProgs/finalcut/src/.libs/libfinal.la'
else
# When we are sourced in execute mode, $file and $ECHO are already set.
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
file="$0"
# A function that is used when there is no print builtin or printf.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
$1
_LTECHO_EOF'
}
ECHO="printf %s\\n"
fi
# Very basic option parsing. These options are (a) specific to
# the libtool wrapper, (b) are identical between the wrapper
# /script/ and the wrapper /executable/ that is used only on
# windows platforms, and (c) all begin with the string --lt-
# (application programs are unlikely to have options that match
# this pattern).
#
# There are only two supported options: --lt-debug and
# --lt-dump-script. There is, deliberately, no --lt-help.
#
# The first argument to this parsing function should be the
# script's ../libtool value, followed by no.
lt_option_debug=
func_parse_lt_options ()
{
lt_script_arg0=$0
shift
for lt_opt
do
case "$lt_opt" in
--lt-debug) lt_option_debug=1 ;;
--lt-dump-script)
lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'`
cat "$lt_dump_D/$lt_dump_F"
exit 0
;;
--lt-*)
$ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
exit 1
;;
esac
done
# Print the debug banner immediately:
if test -n "$lt_option_debug"; then
echo "busy:busy:$LINENO: libtool wrapper (GNU libtool) 2.4.6 Debian-2.4.6-2" 1>&2
fi
}
# Used when --lt-debug. Prints its arguments to stdout
# (redirection is the responsibility of the caller)
func_lt_dump_args ()
{
lt_dump_args_N=1;
for lt_arg
do
$ECHO "busy:busy:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
lt_dump_args_N=`expr $lt_dump_args_N + 1`
done
}
# Core function for launching the target application
func_exec_program_core ()
{
if test -n "$lt_option_debug"; then
$ECHO "busy:busy:$LINENO: newargv[0]: $progdir/$program" 1>&2
func_lt_dump_args ${1+"$@"} 1>&2
fi
exec "$progdir/$program" ${1+"$@"}
$ECHO "$0: cannot exec $program $*" 1>&2
exit 1
}
# A function to encapsulate launching the target application
# Strips options in the --lt-* namespace from $@ and
# launches target application with the remaining arguments.
func_exec_program ()
{
case " $* " in
*\ --lt-*)
for lt_wr_arg
do
case $lt_wr_arg in
--lt-*) ;;
*) set x "$@" "$lt_wr_arg"; shift;;
esac
shift
done ;;
esac
func_exec_program_core ${1+"$@"}
}
# Parse options
func_parse_lt_options "$0" ${1+"$@"}
# Find the directory that this script lives in.
thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'`
test "x$thisdir" = "x$file" && thisdir=.
# Follow symbolic links until we get to the real thisdir.
file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'`
while test -n "$file"; do
destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'`
# If there was a directory component, then change thisdir.
if test "x$destdir" != "x$file"; then
case "$destdir" in
[\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
*) thisdir="$thisdir/$destdir" ;;
esac
fi
file=`$ECHO "$file" | /bin/sed 's%^.*/%%'`
file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'`
done
# Usually 'no', except on cygwin/mingw when embedded into
# the cwrapper.
WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
# special case for '.'
if test "$thisdir" = "."; then
thisdir=`pwd`
fi
# remove .libs from thisdir
case "$thisdir" in
*[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;;
.libs ) thisdir=. ;;
esac
fi
# Try to get the absolute directory name.
absdir=`cd "$thisdir" && pwd`
test -n "$absdir" && thisdir="$absdir"
program='busy'
progdir="$thisdir/.libs"
if test -f "$progdir/$program"; then
# Add our own library path to LD_LIBRARY_PATH
LD_LIBRARY_PATH="/usr/local/src/MyProgs/finalcut/src/.libs:$LD_LIBRARY_PATH"
# Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
# The second colon is a workaround for a bug in BeOS R4 sed
LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /bin/sed 's/::*$//'`
export LD_LIBRARY_PATH
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
# Run the actual program with our arguments.
func_exec_program ${1+"$@"}
fi
else
# The program doesn't exist.
$ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
$ECHO "This script is just a wrapper for $program." 1>&2
$ECHO "See the libtool documentation for more information." 1>&2
exit 1
fi
fi