AndyObtiva/glimmer

[This is more an idea/discussion, less so a specific issue] Unified GUI-DSL for ad-hoc glimmer drop-in-support? Aka designing a class and then simply add glimmer support for different toolkits onto it

rubyFeedback opened this issue · 2 comments

Hey there Andy,

This is more just a rough idea I had. Please feel free to disregard
or ignore if not applicable or for whatever the reason not considered
useful. It's really meant more as an idea as such.

Recently I had a few issues unrelated to glimmer or libui. For instance,
I did set up win10 again on my laptop, because it became, oddly enough,
super-slow suddenly (not sure why, the laptop was cheap so it behaves
quite oddly sometimes, e. g. overheating too).

Now that I set it up right now I had issues getting libui to work; I actually
got the same problem that you had a few months ago with just about the
same issue of ruby not finding fiddle. But anyway, that's not what the
issue here is about, I just wanted to explain the backstory a bit.

So ... now I am slowly setting up things on my laptop again. Right now
it is semi-stuck at "Working on updates 98%" since a few hours ... :P

(The updates issue on windows became soooo annoying ... they really
got so much worse than linux in the last ~15 years... but I digress)

Now, however had, libui does not work right now. I know that tcl/tk,
including ruby bindings, will work. I got them to work in the past as
well but I did not try yet again whether they will work ... waiting on
the updates to finish right now.

So I was thinking ... "what if I could add glimmer-dsl-tk support
as-is on classes"?

For instance:

class SomeGui

  include Glimmer::DSL::Tk # Don't mind the syntax not working, this is just the example

  def initialize
    run
  end

  def run
    button1 = button('Batch-install all my windows addons)
    button1.on_clicked {
      do_batch_install_all_my_windows_addons
    }
  end

  def do_batch_install_all_my_windows_addons
    # do all necessary stuff here to set up everything
  end

end

So, the idea is to designate which GUI to use (in this case tk but
we could easily switch this to, say, gtk or libui or whatever is
supported by glimmer). But retain the very same code base
"internally". So we would kind of have this work for any
toolkit or WWW or whatever glimmer as such supports.

The benefit of this would be that we could have the same unified
code base and just "plug in" different GUIs.

I don't know whether this is currently possible in glimmer yet or
not. If it is then just disregard please. The primary idea here,
though, is to have a kind of unified approach to DSLs in this
regard. The specific API in the DSL can vary, that's not so much
my concern. The focus I have here is more on having SOME kind
of DSL/API that could work easily (at the least the basic stuff,
such as a button. A button should be a button everywhere. That
different toolkits have some unique peculiarities is a separate
issue that I am not so much concerned with for the BASIC idea
here, if you understand what I mean).

The above example uses include, but we can probably make this
even more flexible if we do not have to use any subclassing
or include at all, and simply designate which toolkit to
use in another way, such as a toplevel @variable that keeps
track of the toolkit in use or some other way.

In my own (semi-failed or semi-abandoned) attempt to use a
unified code base I went via "use :gtk" or "use :libui" and allowed
the user to simply change this from the commandline, such as
via --use-gtk or --use-libui. But it's not so important what variant
is used specifically, just that the idea behind this should work.
The API and DSL in use is more a specific design issue; the
underlying idea is more important. A bit like how rack serves
as base for most ruby webframeworks these days.

Anyway. That's it so far with my idea. I may have forgotten
tons of things or perhaps this all works fine already, not
sure. I am just having this idea as I keep on waiting for the
windows update to finish, so I have some spare time ... :P

Oh yes, I forgot to add one thing, because you may wonder why
one would not use the toplevel DSL as-is. The toplevel DSL approach
is perfectly fine; sinatra uses it too.

But in the case that we already have a large class written, it actually
is more convenient to just add-on stuff to that class, such as via
"include" or some other way (I found include more flexible than
subclassing... for instance, class Foo < ::Gtk::Window means that
this subclass approach is harded to gtk, which removes some
flexibility if you know what I mean). So kind of the more flexible
"include as-is" approach, or perhaps even better without any
hardcoded include as-such either. I hope that makes any sense to you.

Trying to unify the DSL across multiple GUI toolkits adds quite a bit of complexity to each DSL's codebase that is not worth the benefits in my opinion in comparison to simply updating GUI code manually by hand in the extremely rare scenarios you would have to switch an application from one GUI toolkit to another.

I mean, it is really simple to switch from this LibUI GUI DSL code:

button('Submit') {
  on_clicked do
    submit_online
  end
}

To this Tk GUI DSL code:

button {
  text 'Submit'

  on('command') do
    submit_online
  end
}

About your other questions regarding Glimmer's support for selecting DSLs, Glimmer already supports that by simply adding the require statement of the DSL you want, activating automatically.

In other words, if you call this before your application:

require 'glimmer-dsl-libui'

Now, when you do include Glimmer, it automatically uses LibUI.

You can simply update that line to this:

require 'glimmer-dsl-tk'

And, now the Tk GUI DSL is used when you do include Glimmer

You can also keep the DSL require out of the code altogether and just use a -r glimmer-dsl-libui when running the app to select DSL from the terminal:

ruby -r glimmer-dsl-libui some_app.rb

In fact, Glimmer supports the idea of activating multiple (non-conflicting) DSLs at the same time, such as SWT, XML, and CSS. You simply add multiple require statements to do so:

require 'glimmer-dsl-swt'
require 'glimmer-dsl-xml'
require 'glimmer-dsl-css'

Now, when you do include Glimmer, you get access to all 3 DSLs in the code (each having its own top-level keyword for entry into the DSL, like shell for SWT, html for HTML, and css for CSS). In fact, I made use of that to embed HTML/CSS in a browser widget in SWT before. I have an example of it under the Multiple DSL Support section in Glimmer:

require 'glimmer-dsl-swt'
require 'glimmer-dsl-xml'
require 'glimmer-dsl-css'

include Glimmer

shell {
  minimum_size 130, 130
  @browser = browser {
    text html {
      head {
        meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
        style {
          css {
            h1 {
              background 'yellow'
            }
          }
        }
      }
      body {
        h1 { "Hello, World!" }
      }
    }
  }
}.open

(Of course, that was just an example. In a real app, you would use include Glimmer in an actual class [not at the top-level namespace] to avoid namespace pollution. So, Glimmer already addresses your concern there with the use of module mixin instead of class inheritance)

Furthermore, Glimmer enables you to disable DSLs and enable DSLs piecemeal if needed after you activated automatically via require statement.

For example:

require 'glimmer-dsl-swt'
require 'glimmer-dsl-xml'
require 'glimmer-dsl-css'

Glimmer::DSL::Engine.disable_dsl(:swt)
Glimmer::DSL::Engine.disable_dsl(:css)
# Now, only the XML DSL is enabled when using `include Glimmer`

# Later, you can re-enable SWT
Glimmer::DSL::Engine.enable_dsl(:swt)
# Now, both XML and SWT are enabled when using `include Glimmer`

p.s. By the way, it is definitely possible to share a GUI DSL between multiple DSLs in Glimmer as already done in Glimmer DSL for SWT and Glimmer DSL for Opal. But, just because something is possible, that doesn't mean it is always a good idea. In the case of SWT & Opal, the benefit was worth it to migrate a desktop app into the web with little effort as that is a very common requirement/scenario. However, in the case of converting between SWT, Tk or LibUI, I am not sure that is common enough to warrant the extra code in the codebase to support it or the divergence from each toolkit's community conventions through unification. It is more likely that it would be a waste of both time and effort to support such a thing given how rarely it is needed. After all, in software engineering everything has a cost, and the benefit has to exceed the cost to make a trade-off worth it. I would rather keep each toolkit with its own DSL style that appeals better to its audience.