[ANN] Bogue, the OCaml GUI

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

Hi!
I’m just starting to use Bogue in a game I’m making, and I was wondering it if was possible to send data from a game to another ml file?
I have variables representing the “last clicked coordinates” and when I reference them in another file, their values don’t update until the Bogue window is closed.
Any advice would be appreciated, thanks!

Hi! I’m not sure I understand your question. Do you want to save data in a config file for keeping them after the player quits the game, and read them back when she starts again,

or do you want to dynamically use your data (the “last clicked coordinates”) in some other function while the game is running?

Hi! Some friends and I are currently working on implementing a small game in OCaml. Most of our code is pure OCaml – we have a separate game loop and terminal interface that updates with each “action” we perform. Recently, we wanted to use a real GUI library and came across Bogue! Unfortunately, our current implementation involves passing the state into the GUI code which writes a static, unmodifiable visual representation of the state, so we end up creating a brand new window with each change to the game’s state. This isn’t exactly what we intended.

We wanted to know if there was a way to rerender specific components and then pass them into the window so that the entire window wouldn’t rerender; instead, only the updated components would change. Thanks for your time!

yes of course; that’s the way Bogue works. every component in fine is rendered into a texture, and if the component hasn’t changed, the texture is re-used directly, so it is very fast to render. The idea is that you don’t need to care about this, this is done automatically by Bogue. For a small game written with bogue, you may have a look at snoke:

2 Likes

for instance if you have a window full of widgets, and if you want to modify the text of one of them, you should just call Widget.set_text on that widget. There is no need to recreate all the widgets!

Similarly if you want to change the position of a Layout, use Layout.setx or Layout.sety, see Bogue.Layout, don’t recreate the layout!

2 Likes

Thank you for your quick response!!

As a piece of constructive criticism (or maybe I will be proven wrong), I tried out Bogue but I needed to make my own widget (a game board) as the existing ones did not suit my needs. To that occasion I discovered that Widget.t is in fact a variant, a sum of all possible types of widgets. This has the consequence that to add a custom widget type, one has to fork Bogue, which seems like a big modularity extensibility* issue to me.

The general idea is to construct new (possibly complicated) Layouts by assembling simple Widgets. I’m not sure what you are trying to achieve exactly, but could you construct your game board this way?

To group several widgets in a Layout, see for instance this part of the tutorials

2 Likes

@ivg gave a great solution to this specific (expression) problem at The shape design problem - #39 by ivg

2 Likes

I needed to arrange squares in a grid, with the squares evenly distributed and separated by small gutters and rows of interstitial space. Clicking the squares should do something (select them), but clicking the interstitial space also should do something (deselect the selected square, if any). That didn’t seem possible with Bogue.

More generally, relying on composition of existing functionalities can only go so far when there is no possibility for extension. I don’t think it is tenable to require users to fork/ask every time they need a feature, which is why I see extensibility as a must-have.

That’s really easy to do. Just use empty widgets for the interstitial spaces (or images, if you want them to look more fancy)

I’m not opposed to having an extension mechanism, but for this case (and it can be generalized) I don’t see the point. Building a layout out of widgets is the way Bogue works, and if you try to build a new widget of your own, it will be probably more difficult (because programming widgets is more low-level).

In other words, the philosophy here is that widgets are your lego bricks, and they should be generic enough to let you build many marvelous things. Do you view your “game board” as a generic brick that can be used to build other things?

Maybe the word “widget” was not so appropriately chosen and leads to confusion. (In some sense, Bogue’s Layouts are also widgets, in the usual GUI terminology.)

1 Like