[Idea] "A GUI toolkit for 21st century"

106 views
Skip to first unread message

Nickolay Ilyushin

unread,
Jun 24, 2019, 6:25:05 AM6/24/19
to Racket Developers
While checking racket's gui possibilities, I've noticed that widgets (in both amount and config) are limited - yes, they can do much enough, but using somewhat different (yet minimalistic) style we can do way more while dropping most of those widgets.

Please note that this is only an idea which seems (at least for me) nice enough to be implemented when you see such frameworks as Qt Quick or Flutter.

1. Canvas/DC changes

1.1: Canvas clipping
Currently, if we want to limit our drawing to a square, we use bitmap-dc% class, which creates a new bitmap which will be then blitted to the canvas or another bitmap.
This can be simplified (for users though) by making a clip operation similar to the one found in nanovg.
Ideally it would use opengl stencil buffer or similar techniques, so we could clip non-square shapes, but as I can see, racket/gui's canvas does not use low-level graphics APIs directly.
Although it is probably possible to allow non-rectangular shapes in other way.
Ideally clip operations should be stored in a stack (aka list (push=cons, peek=car, pop=cdr) in terms of lisp) so you could restore previous clip state - that would allow making multiple widgets which clip their drawing context.

2. Windowing changes

2.1: Window transparency
Ideally it would be possible if we could make a window partially transparent (in some places) so if user wants to have CSD, he could make slightly better window borders. Although this should not be in priority.

2.2: Custom window shape
Supported by most operating systems, this could allow drawing shadows around windows in window managers which do not draw shadows for windows which use CSD, this could also allow passing clicks through context menu windows, like that is done in Gtk3’s popovers.

2.3: Dragging windows
When user makes a click inside of a window, there should be an option to initiate a drag/resize operation – that is definitely possible in Windows (seen at least in WPF, although I do not know if Win32 API supports that) and Linux (should be supported at least by GDK. If not, accessing X11 APIs directly is possible). Please note that this feature is necessary for fast window dragging when using CSD.

3. Core GUI library

3.1: Drawing
The GUI library i suggest implementing is based on a canvas (like QtGui in Qt Quick/Widgets or Skia in Flutter, Chrome, or Android).

3.2: Views
Views are similar to Android’s activities or WPF/UWP’s windows – they can be navigated to or closed to go back to the previous view. Each view has an own style config as shown below.

3.3: Build context
After elements (see later) are created, they should be “built” so they know their actual appearance and content. Build operation should be invoked once drawn data (for example, text or image) or style config in the context changes. Build operation also creates and builds child elements as shown in 2.4.

Context definition might look like that:

(define build-context%
  (class object%
    (super-new)
    (init-field ; values necessary for determining element positions. For example,
                ; that could be done using a list of geometries - when we are
   
            ; inside of a container, we push a new item to the list; when we
   
            ; leave the container, we pop an item from the list; when we finish
  
              ; a widget, we update the topmost item so previously used space cannot
   
             ; be used.
    )))




While style config definition might look like this:

(define style-config%
  (class object%
    (super-new)
    (init-field [primary-color (make-color ...)]
    [secondary-color (make-color ...)]
    [accent-color (make-color ...)]
    [background-color (make-color ...)]
    [text-color (make-color ...)]
    [text-font (make-font …)]))


Please note that style config stores brand-specific values and not all view authors might want to use other’s brand colors. This means that each value should be replaceable with a ‘parent value or similar, so widgets and other views can inherit parent view’s style config values.

3.4: Elements
There could be a small amount of premade elements which can be used in building more complex elements and widgets. I suggest using: text element, pane element (similar to div in html, can use styles from style config and/or use own values, including stuff like border width/radius, shadow, etc), image element, video element, text input element (possibly in 2 variations – text line and text editor), view element to embed a view as a part of a current one, and canvas element for more complex rendering (like combo boxes).

make-element function will create a new element with a build callback which creates a portion of other elements, similar to how its done in Flutter or React.

(define (make-element [build-callback])
   ...)

(make-element #:build-callback
  (lambda ()
    (new pane%
      (new text% [text "Foo"]))))

4: What this will allow
4.1: Implementing beautiful widget sets like those defined in material or fluent design specifications.
4.2: Easily implementing tons of custom widgets.
4.3: Building brand-flavored applications and views to be used in other applications, which could be easily integrated with other application's theme.

For example, we are writing a contacts application. While having an own launcher and a widow, it could provide views which can be used in another application to show, for example, a contact card, a contact list, etc. This embedded view widget could also use parts of host application theme while keeping bits of brand-specific stuff.

Alex Harsanyi

unread,
Jun 24, 2019, 8:23:52 PM6/24/19
to Racket Developers

On Monday, June 24, 2019 at 6:25:05 PM UTC+8, Nickolay Ilyushin wrote:
While checking racket's gui possibilities, I've noticed that widgets (in both amount and config) are limited - yes, they can do much enough, but using somewhat different (yet minimalistic) style we can do way more while dropping most of those widgets.

Please note that this is only an idea which seems (at least for me) nice enough to be implemented when you see such frameworks as Qt Quick or Flutter.

1. Canvas/DC changes

1.1: Canvas clipping
Currently, if we want to limit our drawing to a square, we use bitmap-dc% class, which creates a new bitmap which will be then blitted to the canvas or another bitmap.
This can be simplified (for users though) by making a clip operation similar to the one found in nanovg.
Ideally it would use opengl stencil buffer or similar techniques, so we could clip non-square shapes, but as I can see, racket/gui's canvas does not use low-level graphics APIs directly.
Although it is probably possible to allow non-rectangular shapes in other way.

The draw context for a canvas (which you can get via the `get-dc` canvas% method) has a `set-clipping-rect` and `set-clipping-region` methods to clip to a rectangle or an arbitrary path.
 
Ideally clip operations should be stored in a stack (aka list (push=cons, peek=car, pop=cdr) in terms of lisp) so you could restore previous clip state - that would allow making multiple widgets which clip their drawing context.

There is no stack for the clipping regions, but each function that does some drawing could save the old clipping region, install a new one, than restore the old one once done:

(let ((old-region (send dc get-clipping-region)))
  (send dc set-clipping-region new-region)
  ;; do the drawing here
  (send dc set-clipping-region old-region))

A similar approach can be used to set pens, brushes and other drawing resources and a lot of higher level drawing libraries already use this technique.

Alex.
Reply all
Reply to author
Forward
0 new messages