AndyObtiva / glimmer-dsl-libui

Glimmer DSL for LibUI - Prerequisite-Free Ruby Desktop Development Cross-Platform Native GUI Library - The Quickest Way From Zero To GUI - If You Liked Shoes, You'll Love Glimmer! - No need to pre-install any prerequisites. Just install the gem and have platform-independent GUI that just works on Mac, Windows, and Linux.
MIT License
516 stars 16 forks source link

Grid not showing up as expected #54

Open seydar opened 1 year ago

seydar commented 1 year ago

I am 100% sure that this is a PEBKAC case, but I'm not sure where else to turn:

require 'glimmer-dsl-libui'
include Glimmer

window("Test", 500, 500) {
  grid {

    area {
      left 1
      top 0

      rectangle(0, 0, 150, 500) {
        fill 0xff0000
      }
    }

    non_wrapping_multiline_entry {
      left 0
      top 0

      text "asdf"
    }
  }
}.show

Shows up as the attached image.

What's happening: the rectangle is all the way to the left What should happen: the rectangle should begin within its spot in the grid.

Obviously, I recognize that this is just my unfamiliarity with library, but I'm hoping for some guidance on where to turn to.

Screen Shot 2023-08-09 at 6 32 42 PM
seydar commented 1 year ago

I guess I have a couple usage questions:

seydar commented 1 year ago

Here's another example; it should be a green rectangle above a red rectangle, but the red rectangle doesn't appear.

require 'glimmer-dsl-libui'

class Test
  include Glimmer

  def launch
    window("Test", 500, 500) {
      margined true

      grid {

        area {
          left 0; xspan 1
          top  0; yspan 1
          rectangle(0, 0, 500, 200) {
            fill 0x00ff00
          }
        }

        area {
          left 0; xspan 1
          top  1; yspan 1

          rectangle(0, 0, 500, 200) {
            fill 0x0000ff
          }
        }

      }

    }.show
  end
end

Test.new.launch
Screen Shot 2023-08-11 at 3 35 58 PM
AndyObtiva commented 1 year ago

Thank you for submitting this issue.

The last scenario you shared is definitely not supported. It is mentioned in the Glimmer DSL for LibUI API gotchas: https://github.com/AndyObtiva/glimmer-dsl-libui#api-gotchas

It seems that the C libui library does not support nesting multiple area controls under a grid as only the first one shows up in that scenario. To workaround that limitation, use a vertical_box with nested horizontal_boxs instead to include multiple areas in a GUI.

From doing some testing, it seems non_wrapping_multiline_entry does not play well with grid either, but entry and label do.

I personally learned to avoid using grid and just use a combination of horizontal_box and vertical_box, sometimes including empty label controls if I needed to add blank space.

Given that the underlying C libui library is still alpha, it seems like the grid layout is not ready for prime time yet, especially when including area or non_wrapping_multiline_entry in it.

On the other hand, Glimmer DSL for SWT's grid layout is 100% feature complete and solid, including the ability to specify the size of each grid in pixels using layout data. You could try it instead if you're willing to build apps with JRuby.

grid layout

For example, I built this app using the SWT grid layout, taking advantage of every advanced feature in it:

Math Bowling 2: https://github.com/AndyObtiva/MathBowling math bowling

But, going back to Glimmer DSL for LibUI, like I mentioned, I would recommend just using a combination of horizontal_box and vertical_box.

If you could provide me with a quick mockup or description of the graphical user interface you are trying to build, maybe I could advise you on how to build it using Glimmer DSL for LibUI.

If you haven't checked the project examples already, I highly recommend checking out the project examples as that's the fastest way to learn it.:

Tetris is a good example of using area and non-area controls: https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#tetris

Tetris

seydar commented 1 year ago

Fantastic! Thank you very much for the response.

From doing some testing, it seems non_wrapping_multiline_entry does not play well with grid either, but entry and label do.

I personally learned to avoid using grid and just use a combination of horizontal_box and vertical_box, sometimes including empty label controls if I needed to add blank space.

Given that the underlying C libui library is still alpha, it seems like the grid layout is not ready for prime time yet, especially when including area or non_wrapping_multiline_entry in it.

This is what I was looking to hear — glad I'm not crazy.

Unfortunately, I'm building a GUI for a tool I wrote that leans on the NArray C library in a non-trivial way, so I can't use JRuby/SWT. I've been wrestling with grid and have gotten it to finally work, although it wasn't easy. Here's what I have so far:

Screen Shot 2023-08-13 at 3 22 11 PM

I would like to use a combination of horizontal_box and vertical_box, but I don't know how to give them uneven space (like the table on the left and the large graph of vertexes and edges) — my attempts for the above image end up splitting the screen in half. However, you manage to do it in tetris.rb, so I'm going to play around some more and see what I can make happen.

Thanks again!

AndyObtiva commented 1 year ago

Wow! This GUI you built looks amazing! You're some sort of a genius to have figurd this out. Could I include that screenshot in Glimmer DSL for LibUI's Applications? Also, is it an open-source software project by any chance? Would you mind at least sharing how you got the grid working (view code or part of it only)?

By the way, with horizontal_box and vertical_box, in order to ensure not all controls have the same size, you set the property stretchy false for ones that shouldn't stretch to take up all available remaining space. However, I just tested it with area, and unfortunately, the stretchy option applied to table (to prevent it from stretching) does not work correctly when an area is inside the same horizontal_box along with it. I reported the issue (and other issues) to the upstream libui-ng C library project: https://github.com/libui-ng/libui-ng/issues/216

seydar commented 1 year ago

I'm so glad you like it, thank you so much! Please feel free to include the screenshot wherever you want!

I would like it to be open source, but I'm trying to use this software to maybe start a company, so I can't quite release it yet until I figure out how to somehow find work with the underlying software.

That said, I'm working on rewriting the GUI to use MVP, so I can definitely make the GUI open source. I'll have it up shortly and let you know.

AndyObtiva commented 1 year ago

It doesn't have to be open-source at all. I only asked just in case so I'd learn more about the project. But, I respect closed-source software too, especially when it's for a business. I work for a company with closed source software, so I totally understand. You don't have to open-source it.

Thank you for allowing me to share the screenshot. I added it under Glimmer DSL for LibUI Applications: https://github.com/AndyObtiva/glimmer-dsl-libui/tree/master#electric-avenue

seydar commented 1 year ago

Here's the GUI code for it (I just tried to rewrite it to be more like MVP with observers, but it's tough — I'm not used to this style, or GUI programming in general).

https://github.com/seydar/electric_gui

I'm open to any feedback you have on how I should structure things.

AndyObtiva commented 1 year ago

OK, thank you for sharing. I will take a look when I get a chance.

AndyObtiva commented 1 year ago

I took a look.

Your app.rb generally looks good. I like how you abstracted components out into methods.

In Glimmer DSL for LibUI terminology, this technique is called Method-Based Custom Keywords. Or, more casually, method-based custom controls (components).

Also, I liked your usage of data-binding and how it pulled data cleanly from pure presenters (overall.rb and congestion.rb). It was very well done.

I noticed that you declared histogram.rb (and plotter.rb) as a Presenter. However, it is used as a View given that it is rendering GUI. Usually, Presenters present information that is summarized from Models as pure data, and then that pure data is rendered as GUI in Views outside of Presenters (a layer above). And, Presenters generally don't mix in Glimmer. They're a layer below Glimmer (which is only mixed into Views in general).

You could have avoided that by extracting this bit of code from app.rb into histogram.rb, and putting histogram.rb under a views directory.

    @hist = area {
      left x; xspan xs
      top  y; yspan ys
      vexpand true

      on_draw do |area|
        rectangle(0, 0, area[:area_width], area[:area_height]) {
          fill 0xFFFFFF
        }

        @histogram.plot area
      end
    }

The same could have been done for plotter.rb:

    @plot = area {
      left x; xspan xs
      top  y; yspan ys

      on_draw {|area|
        @plotter.scale_area area, PLOT
        self.desc = grid_description

        # Background
        rectangle(0, 0, area[:area_width], area[:area_height]) {
          fill 0xffffff
        }

        # Graph
        @plotter.plot_flows

        # Info
        @plotter.plot_info_box
      }

      on_mouse_up do |area_event|
        @plotter.select_info_box(area_event[:x], area_event[:y])
        @plot.queue_redraw_all
      end
    }

To do that, you could use the other Glimmer DSL for LibUI custom control technique, which is called Class-Based Custom Keywords, or class-based custom controls. Glimmer will automatically convert such classes into underscored keywords. So, if you declare a Histogram custom control, you can later use it with histogram in a View that mixes Glimmer. A Plotter class becomes plotter in Glimmer View code.

This code is a small example of how to build a class-based custom control (it generates the address_view keyword):

class AddressView
  include Glimmer::LibUI::CustomControl

  options :address

  body {
    vertical_box {
      address.each_pair do |attribute, value|
        label_pair(model: address, attribute: attribute, value: value)
      end
    }
  }
end

This can later be used from another View like this:

  window {
    horizontal_box {
      address_view(address: shipping_address)
      address_view(address: billing_address)
    }
  }

To learn more how to build class-based custom controls, I highly recommend checking out the examples/class_based_custom_controls.rb example: https://github.com/AndyObtiva/glimmer-dsl-libui/blob/master/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#class-based-custom-controls

Another bit of feedback is app.rb could have taken advantage of the new Glimmer DSL for LibUI module for building applications Glimmer::LibUI::Application (aka Glimmer::LibUI::CustomWindow): https://github.com/AndyObtiva/glimmer-dsl-libui#usage

It expects declaring the GUI code cleanly inside body, and it takes care of the rest whe you call ClassName.launch (it also supports before_body and after_body hooks in case there is a need to execute code before or after initialization of the body, so any code in initialize goes into before_body if you refactor).

require 'glimmer-dsl-libui'

class SomeGlimmerApp
  include Glimmer::LibUI::Application

  body {
    window('hello world', 300, 200) {
      button('Button') {
        on_clicked do
          puts 'Button Clicked'
        end
      }
    }
  }
end

SomeGlimmerApp.launch

In summary:

I hope that I provided enough feedback. If you have any further questions, feel free to ask.

Cheers.