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
497 stars 15 forks source link

Support for spinner-like widget in glimmer-dsl-libui? #20

Closed rubyFeedback closed 2 years ago

rubyFeedback commented 2 years ago

Hey there Andy,

I already wrote a proposal for libui-ng in regards to spinner-widgets.

In ruby-gtk3 the spinner widget can be used to indicate an activity is ongoing.

See here as an example for the spinner in gtk:

https://docs.gtk.org/gtk4/class.Spinner.html

I use this, for example, to a GUI where the user can modify meta-data of a .mp4 file, via ffmpeg. That part works fine.

Now I was thinking to make this available on windows too. ffmpeg.exe works on windows, I already know that.

So my idea was to use glimmer-dsl-libui for that. But as far as I am aware there is no spinner functionality in upstream libui. Would it be possible to simulate or fake such a spinner widget, in glimmer-dsl-libui?

I don't have a good way to propose this, but two ideas I have on the top of my head are:

or alternatively:

Does not have to look pretty. The main idea behind the spinner is to indicate to the user that the application is still working, so the user does not get bored and knows that things are still happening as-is.

I don't know how easy it would be for glimmer-dsl-libui to support it or whether you think it is useful. The above two points in regards how to implement it are just examples; perhaps there are more ways to think about this.

AndyObtiva commented 2 years ago

You can simply implement a spinner with area vector graphics using Glimmer::LibUI::timer.

I added a section to the README called Area Animation that documents how to do it.

Spinner example that has a fully customizable method-based custom control called spinner, which is destroyed if you click on it (you may copy/paste in girb):

require 'glimmer-dsl-libui'

class SpinnerExample
  include Glimmer

  SIZE = 120

  def initialize
    create_gui
  end

  def launch
    @main_window.show
  end

  def create_gui
    @main_window = window {
      title 'Spinner'
      content_size SIZE*2, SIZE*2

      horizontal_box {
        padded false

        vertical_box {
          padded false

          spinner(size: SIZE)
          spinner(size: SIZE, fill_color: [42, 153, 214])
        }

        vertical_box {
          padded false

          spinner(size: SIZE/2.0, fill_color: :orange)
          spinner(size: SIZE/2.0, fill_color: {x0: 0, y0: 0, x1: SIZE/2.0, y1: SIZE/2.0, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 1, r: 2, g: 2, b: 254}]})
          spinner(size: SIZE/2.0, fill_color: :green, unfilled_color: :yellow)
          spinner(size: SIZE/2.0, fill_color: :white, unfilled_color: :gray, background_color: :black)
        }
      }
    }
  end

  def spinner(size: 40.0, fill_color: :gray, background_color: :white, unfilled_color: {r: 243, g: 243, b: 243}, donut_percentage: 0.25)
    arc1 = arc2 = nil
    area { |the_area|
      rectangle(0, 0, size, size) {
        fill background_color
      }
      circle(size/2.0, size/2.0, size/2.0) {
        fill fill_color
      }
      arc1 = arc(size/2.0, size/2.0, size/2.0, 0, 180) {
        fill unfilled_color
      }
      arc2 = arc(size/2.0, size/2.0, size/2.0, 90, 180) {
        fill unfilled_color
      }
      circle(size/2.0, size/2.0, (size/2.0)*(1.0 - donut_percentage)) {
        fill background_color
      }

      on_mouse_up do
        the_area.destroy
      end
    }.tap do
      Glimmer::LibUI.timer(0.05) do
        delta = 10
        arc1.start_angle += delta
        arc2.start_angle += delta
      end
    end
  end
end

SpinnerExample.new.launch

mac spinner

As you can see, the spinner custom control is fully customizable with options like size (you can set to 20 if you like) and fill_color (which can even be a gradient).

If you click on any of the spinners, they are destroyed. This is how you can remove them in your application once there is no more work to do that needs a spinner, by calling the #destroy method on the area object.

To add them, you simply call code like this (assuming you declared a some_box variable as vertical_box or horizontal_box):

some_box.content { spinner(size: 20) }

BTW, it works on Windows too:

glimmer-dsl-libui-windows-spinner

Also, it works on Linux:

glimmer-dsl-libui-linux-spinner

If there is anything that is not clear enough or you have any further questions, feel free to ask me by replying to this issue (even if closed) or opening a new one if needed.

Cheers!