[ANN] Bogue, the OCaml GUI

Looking at the dependencies, I noticed that you also wrote a binding to SDL_ttf.
I am wondering if it would be enough to provide a SDL-based reimplementation of the Graphics library (with exactly the same interface)? It could be a nice way to have a pedagogical graphical API which is just a front-end for a full graphical library.

4 Likes

Yes, I think it would be fairly easy.
Looking at the Graphics API, the only things that are not included in SDL+SDL_ttf+SDL_image
are graphics primitives like (filled) ellipses and (filled) polygons. One could write bindings to SDL_gfx (*) for these, but it’s more fun to program them directly in ocaml :wink:

(*) in fact they already exist, GitHub - fccm/tsdl-gfx: OCaml SDL2_gfx bindings to go with Tsdl

2 Likes

I’m drawing things like this using the graphics library (for now)


So I’m just drawing circles, lines from a point to another and text.
Would it be possible to add a widget in which I could draw this kind of things to a Bogue window with other widgets ?

1 Like

Yes, absolutely. The Box widget can contain an arbitrary SDL texture, and hence you may use all SDL drawing functions onto that texture. Unfortunately, this is not documented yet (simply because my strategy is to add to the .mli file only functions that are tested in examples). But I can add this to the next release :wink:

4 Likes

That would be awesome, thanks ! Do you know how I can be notified for the next release ?

I will announce the next releases in this thread

1 Like

Sounds nice ! You could even release a standalone project that would only provide this API in a single window, as an alternative to graphics : that would be a godsend imo, because the api of graphics really is terrible

In fact, for this, using the already existing Bogue version, one can use Labels with a round background. This gives the following picture: it would just remain to implement lines… :wink:

Here is the complete code, for the curious ones:

open Bogue
module W = Widget
module L = Layout

let n = 15 (* number of discs *)
let radius = 20
let width = 800
let height = 600

let c = Draw.find_color "#e5b92c"
let disc_style = Style.(
    create ~border:(
      mk_border ~radius (mk_line ~color:Draw.(opaque c) ~width:1 ~style:Solid ()))
      ~background:(color_bg Draw.(transp c)) ())

let background = L.style_bg Style.(
    of_bg (gradient ~angle:45. Draw.[opaque grey; opaque black]))

let fg = Draw.(opaque white)

let create_disc i (x,y) =
  let w = 2*radius + 1 in
  let bg = Box.create ~style:disc_style ~width:w ~height:w () in
  W.label ~fg (string_of_int i)
  |> L.resident ~background:(L.box_bg bg) ~x:(x-radius) ~y:(y-radius) ~w ~h:w

let random_center _ =
  radius + Random.int (width - 2*radius),
  radius + Random.int (height - 2*radius)

let area =
  let box = L.resident (W.box ~w:width ~h:height ()) in
  let centers = Array.init n random_center in
  let discs = Array.mapi create_disc centers |> Array.to_list in
  L.superpose ~w:width ~h:height ~background (box :: discs)

let board = Bogue.make [] [area]

let () = Bogue.run board
3 Likes

I’m not sure I understand what you have in mind. If you want to do graphics in a window with the SDL library, you can just import the tsdl, tsdl-image and tstl-ttf packages and you’re good to go.

Nice ! Should I use boxes for lines ?

In theory indeed you could make a horizontal line with a thin box and then use Layout.rotate.
but I think it make sense (for me) to propose a new, dedicated Line widget.

1 Like

Hmm OK, I’ll wait for your next release then. Thanks again !

Hi! Can you point me to the documentation about setting up themes? When I do

$ boguex 0

I get

Loading Bogue 20220101 with config dir /home/rsmith/.opam/4.12.1/lib/bogue/../../share/bogue/themes/default 
INFO: Using SDL 2.0.14
0 = Just a check button.
Fatal error: exception Failure("SDL ERROR: Couldn't open /home/rsmith/.opam/4.12.1/lib/bogue/../../share/bogue/themes/default/check_on.png")

I’m excited about testing this.

sorry for this, this is what my post [ANN] Bogue, the OCaml GUI - #4 by sanette was about: you have to install the github version for now…

(download, cd bogue and opam install . should be enough)

EDIT: Doing
export BOGUE_THEME=default
should also work

Yeah, but having a dedicated library for beginner that hides this would be useful in my opinion.

Hi @Regis_Smith the new version in opam should fix this issue.
(version 20220115)

It should also work on Mac Retina screens, thanks to the help of @nilsbecker!

EDIT: the doc about Theming is here: Bogue.Theme

1 Like

Hi, some new developments. I have implemented a new Sdl_area widget where one can conveniently issue any SDL function (from the SDL Renderer API).

Here is (below) the new ‘labelled graph’ example. In this example I am using regular “label” widgets for creating the nodes, and I am using an Sdl_area for drawing the lines.

The nice things for labels to be regular widgets is that one can click on them. To demonstrate this, in this example they react to a click by jumping to another random location (with animation).

graph-click

open Bogue
module W = Widget
module L = Layout

let n = 15 (* number of discs *)
let radius = 20
let width = 800
let height = 600

let c = Draw.find_color "#e5b92c"
let cb = Draw.find_color "#7b6b35"
let disc_style = Style.(
    create ~border:(
      mk_border ~radius (mk_line ~color:Draw.(opaque c) ~width:1 ~style:Solid ()))
      ~background:(color_bg Draw.(opaque cb)) ())

let background = L.style_bg Style.(
    of_bg (gradient ~angle:45. Draw.[opaque grey; opaque black]))

let fg = Draw.(opaque white)

let create_disc i (x,y) =
  let w = 2*radius + 1 in
  let bg = Box.create ~style:disc_style ~width:w ~height:w () in
  W.label ~fg (string_of_int i)
  |> L.resident ~background:(L.box_bg bg) ~x:(x-radius) ~y:(y-radius) ~w ~h:w

let move_disc (x,y) d =
  let (x0, y0) = L.xpos d, L.ypos d in
  L.animate_x d (Avar.fromto x0 x);
  L.animate_y d (Avar.fromto y0 y)

let random_center _ =
  radius + Random.int (width - 2*radius),
  radius + Random.int (height - 2*radius)

let area =
  let sdlw = W.sdl_area ~w:width ~h:height () in
  let sdla = W.get_sdl_area sdlw in
  let centers = Array.init n random_center in
  let color = Draw.(opaque grey) in
  let draw_lines renderer = let open Draw in
    for i = 0 to n - 2 do
      let x0, y0 = to_pixels centers.(i) in
      let x1, y1 = to_pixels centers.(i+1) in
      line renderer ~color ~thick:6 ~x0 ~y0 ~x1 ~y1
    done in
  Sdl_area.add sdla draw_lines;
  let discs = Array.mapi create_disc centers |> Array.to_list in
  (* move the disc when click on it *)
  List.iteri (fun i d ->
      W.on_click ~click:(fun _ ->
          centers.(i) <- random_center 0;
          Sdl_area.update sdla;
          let x,y = centers.(i) in
          move_disc (x - radius, y - radius) d) (L.widget d))
    discs;
  L.superpose ~w:width ~h:height ~background (L.resident sdlw :: discs)


let board = Bogue.make [] [area]

let () = Bogue.run board
8 Likes

Thank you so much, I look forward to implementing this in my current project!
I’ll let you know how it goes

Cool. Is it possible with this API to wait with drawing the new connecting lines to the randomized label until the animation has finished?

Yes, it is possible, because Avar.fromto has an ending parameter.
You would simply have to replace the line containing L.animate_x by

 L.animate_x d (Avar.fromto x0 x ~ending:(fun () ->
      Sdl_area.update sdla;
      Update.push sdlw));

add sdla and sdlw as parameters to the move_disc function (in fact only sdlw is necessary), and remove the line with Sdl_area.update sdla below.

2 Likes