module Widget:sig
..end
Creating widgets and giving life to them
Widgets are simple graphic elements that can react to user interaction. They are
the inhabitants of your GUI house. When a widget is woken up by some event,
it can talk to another widget by means of a connection
.
type
t
The type Widget.t
is a union of all kinds of widgets: Box, Button, Check box,
Image, Label, Slider, Text display, and Text input, plus the Empty
widget.
For instance, in the following code:
let w = label "Hello" in
let l = get_label w in
...
w
has the generic Widget.t
type, while l
has the specialized Label.t
type.
A connection has a source widget and a target widget. When the source
widget receives a specified event, the connection is activated, executing a
specified function, which is called Widget.action
.
An action is always executed in a new Thread (and hence will not block the
GUI), unless the priority Main
is specified.
If a widget possess several connections which react to the same event, the order of execution of these connections is the same as the order they were registered.
type
connection
typeaction =
t -> t -> Tsdl.Sdl.event -> unit
An action is a function with three parameters w1 w2 ev
, where
w1
is the source widget, w2
the target widget, and ev
the event
(Trigger.t
) that triggered the action.
The action should regularly verify Trigger.should_exit
ev
and quickly
exit when that function returns true
.
What happens when an event triggers an action while the same action (=
same connection id) is already running? Several behaviours are possible,
depending on the following Widget.action_priority
type.
type
action_priority =
| |
Forget |
(* | discard the new action | *) |
| |
Join |
(* | execute the new after the first one has completed | *) |
| |
Replace |
(* | kill the first action (if possible) and execute the second one | *) |
| |
Main |
(* | run in the main program. So this is blocking for all subsequent actions | *) |
val connect : t ->
t ->
action ->
?priority:action_priority ->
?update_target:bool ->
?join:connection ->
Trigger.t list -> connection
connect source target action triggers
creates a connection from the
source
widget to the target
widget, but does not register it (this
may change in the future...). Once it is registered (either by
Main.create
or Widget.add_connection
), and assuming that the layout
containing the source widget has focus, then when an event ev
matches one of the triggers
list, the action
is executed with
arguments source target ev
.
priority
: indicates the desired priority policy. Default is Forget
.val connect_main : t ->
t ->
action ->
?update_target:bool ->
?join:connection ->
Trigger.t list -> connection
Alias for connect ~priority:Main
. Should be used for very fast actions
that can be run in the main thread.
val add_connection : t -> connection -> unit
Registers the connection with the widget. This should systematically be
done after each connection creation, when the connection is created after Main.create
.
Connections that are created before Main.create
should rather be
passed as argument to Main.create
, and not via
add_connection
. Although this is not strictly necessary, this indicates
that these connections are more 'pure' or at least more static, in the
sense that they will not be modified by Bogue. These are usually much
easier to debug.
add_connection
is separated from Widget.connect
because it is not pure: it
mutates the widget. This might change in future versions.
val update : t -> unit
update w
asks the widget w
to refresh at next frame. The most probable
use of update
is within the code of an Widget.action
. It can happen that the
action modifies the visual state of a widget that is neither the source or
the target, and then one needs to explicitly tell this widget to re-draw
itself.
val on_release : release:(t -> unit) -> t -> unit
on_release ~release:f w
registers on the widget w
the action f
,
which will be executed when the mouse button is released on this widget.
Uses priority=Main
release:(t -> unit) -> t -> unit
: Similar to Widget.on_release
but specialised to button widgets. It also checks
the key used to activate buttons (currently, the Return key).
val on_click : click:(t -> unit) -> t -> unit
Uses priority=Main
val mouse_over : ?enter:(t -> unit) ->
?leave:(t -> unit) -> t -> unit
As a general rule, widgets should be created using the functions below, which
belong to the Widget module and create an element of type Widget.t
. However, for
some specialized usage, additional features may be available from the widget
underlying module (eg. Label
, Box
, etc.).
See the conversion functions below.
val box : ?w:int -> ?h:int -> ?style:Style.t -> unit -> t
Create a Box widget, which simply displays a rectangle, optionally with
rounded corners and drop shadow. It is often used for the background of a
group of widgets (i.e. a Layout.t
).
The standard on/off check boxes.
val check_box : ?state:bool -> ?style:Check.style -> unit -> t
val set_check_state : t -> bool -> unit
Use this for multi-line text.
val text_display : ?w:int -> ?h:int -> string -> t
val rich_text : ?size:int ->
?w:int -> ?h:int -> Text_display.words list -> t
val verbatim : string -> t
val html : ?w:int -> ?h:int -> string -> t
Display basic html text by interpreting the following tags: <em>,</em>,
and also a color
selector with
<b>,</b>, <strong>,</strong>, <u>, </u>, <p>,</p>, <br><font color="???">, </font>
. The "???" string should be
replaced by a color code, either RGB like "#40E0D0" of "#12C" or RGBA, or
a color name like "darkturquoise".
See `boguex 47`.
val label : ?size:int ->
?fg:Draw.color ->
?font:Label.font ->
?style:Label.style ->
?align:Draw.align -> string -> t
Create a Label widget with a one-line text.
val icon : ?size:int -> ?fg:Draw.color -> string -> t
Create a Label widget with a FontAwesome icon.
For instance icon ~size:24 "star"
creates a widget that displays the
"fa-star" fontawesome icon.
val empty : w:int -> h:int -> unit -> t
Create a widget that does not display anything but still gets focus and reacts to events.
val image : ?w:int ->
?h:int ->
?bg:Draw.color ->
?noscale:bool -> ?angle:float -> string -> t
Load image file.
val image_from_svg : ?w:int -> ?h:int -> ?bg:Draw.color -> string -> t
Requires rsvg
.
val image_copy : ?rotate:float -> t -> t
Return a new "Image" widget linked to the same image (same underlying
Image.t
, hence same texture.)
val text_input : ?text:string ->
?prompt:string ->
?size:int ->
?filter:Text_input.filter -> ?max_size:int -> unit -> t
size
is the font size. max_size
is the maximum number of chars
allowed. The prompt
is used to display a message when there is no user
input. It also influences the size of the widget.
?kind:Button.kind ->
?label:Label.t ->
?label_on:Label.t ->
?label_off:Label.t ->
?fg:Draw.color ->
?bg_on:Style.background ->
?bg_off:Style.background ->
?bg_over:Style.background option ->
?state:bool ->
?border_radius:int ->
?border_color:Draw.color ->
?action:(bool -> unit) -> string -> t
: val slider : ?priority:action_priority ->
?step:int ->
?value:int ->
?kind:Slider.kind ->
?var:(int Avar.t, int) Tvar.t ->
?length:int ->
?thickness:int ->
?tick_size:int -> ?lock:bool -> ?w:int -> ?h:int -> int -> t
val slider_with_action : ?priority:action_priority ->
?step:int ->
?kind:Slider.kind ->
value:int ->
?length:int ->
?thickness:int ->
?tick_size:int -> action:(int -> unit) -> int -> t
Create a slider that executes an action each time the local value of the slider is modified by the user.
You can use an Sdl_area widget to draw whatever you want using all the power of the SDL Renderer API.
val sdl_area : w:int -> h:int -> ?style:Style.t -> unit -> t
See Sdl_area.create
regarding the size (w,h)
.
val check_box_with_label : string -> t * t
let b,l = check_box_with_label text
creates a check box b
, a label
l
, and connect them so that clicking on the text will also act on the
check box.
These generic functions work on all types of widgets, and emit an error in the log (without raising any exception) whenever the type of the argument makes no sense for the function.
These functions are very handy, but sometimes can hide a bug. For instance
if you want to use get_state t
, while you know that t
should always be
of type Button
, then it will help debugging to use instead the slightly
longer form Button.state
(get_button t)
. Indeed the latter will fail
if t
happens not to be a Button.
val get_state : t -> bool
Query a boolean state. Works for Button and Check.
val get_text : t -> string
Return the text of the widget. Works for Button, TextDisplay, Label, and TextInput.
val size : t -> int * int
If the widget is not rendered yet, a default size may be returned instead of the true size.
val set_state : t -> bool -> unit
Set a boolean state. Works for Button and Check.
val set_text : t -> string -> unit
Change the text of a widget. Works for Button, TextDisplay, Label, and TextInput.
val set_cursor : t -> Tsdl.Sdl.cursor option -> unit
Set the cursor that should be displayed for this widget. Note that the Sdl
functions for creating cursor are only available after SDL
initialization. One can use a Lazy
type or Sync.push
for delaying
their execution.
These functions raise Invalid_argument
whenever their argument is not of
the correct type.
val get_box : t -> Box.t
val get_check : t -> Check.t
val get_label : t -> Label.t
t -> Button.t
: val get_slider : t -> Slider.t
val get_text_display : t -> Text_display.t
val get_text_input : t -> Text_input.t
val get_image : t -> Image.t
val get_sdl_area : t -> Sdl_area.t
val map_text : (string -> string) -> action
map_text f
is a Widget.action
that replaces the text of the second widget
by f
applied to the text of the first widget.