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

Reset/Untoggle a checkbox #22

Closed laurabernardy closed 2 years ago

laurabernardy commented 2 years ago

Hi,

I am a RUBY beginner using the glimmer-dsl-libui for a NLP-program and have a question: how do I uncheck the checkboxes afterwards in the GUI? (even if I set the value checked to false, it is not possible to update the view) I've tried also data-binding, but there's no description in the documentary and i don't get the transfer.

Thanks

AndyObtiva commented 2 years ago

If you are using table checkboxes in Windows or Mac, then please keep in mind the limitation mentioned under API Gotchas: "table checkbox_text_column checkbox editing only works on Linux (not Mac or Windows) due to a current limitation in libui." until fixed sometime in the future. The issue should not affect you on Linux though.

Also, if you are using a standard checkbox, then you can only check it through the GUI (graphical user interface) at the moment, but you cannot programmatically check/uncheck. That is also a libui limitation that should be addressed sometime in the future.

Here is a basic checkbox example (without programmatic checking of the checkbox):

require 'glimmer-dsl-libui'

include Glimmer

window('Checkbox', 200, 50) { |main_window|
  margined true

  vertical_box {
    checkbox('Checkbox') { |cb|
      stretchy false

      on_toggled do
        checked = cb.checked?
        main_window.title = "Checkbox is #{checked}"
        cb.text = "I am #{checked ? 'checked' : 'unchecked'}"
      end
    }
  }
}.show

Screen Shot 2022-03-07 at 1 06 13 PM

Screen Shot 2022-03-07 at 1 06 16 PM

Screen Shot 2022-03-07 at 1 06 18 PM

That said, there is a workaround that should solve your problem.

You can use a graphical checkbox instead if you want to programmatically check it and uncheck it.

That can be done using the area control.

Here is an example of a graphical checkbox that is checked/unchecked when pressing buttons:

require 'glimmer-dsl-libui'

class GraphicalCheckboxExample
  include Glimmer

  def launch
    window('Graphical Checkbox', 200, 50) { |main_window|
      margined true

      vertical_box {
        checkmark_lines = graphical_checkmark

        button('Uncheck') {
          on_clicked do
            checkmark_lines.each {|l| l.stroke = :white}
          end
        }

        button('Check') {
          on_clicked do
            checkmark_lines.each {|l| l.stroke = :black}
          end
        }
      }
    }.show
  end

  def graphical_checkmark
    checkmark_line1 = checkmark_line2 = nil
    area {
      rectangle(0, 0, 20, 20) {
        fill :white
      }

      checkmark_line1 = line(0, 0, 20, 20) {
        stroke :black
      }

      checkmark_line2 = line(20, 0, 0, 20) {
        stroke :black
      }
    }
    [checkmark_line1, checkmark_line2]
  end
end

GraphicalCheckboxExample.new.launch

Screen Shot 2022-03-07 at 1 20 57 PM

Screen Shot 2022-03-07 at 1 20 59 PM

laurabernardy commented 2 years ago

Thanks for the quick and detailed answer! Since the Application is mostly used on Windows and Mac systems, i circumwent the unchecking of the Basic Boxes, ment for resetting the Application, by restarting the Script.

mohad12211 commented 2 years ago

Hey! can I know what's the limitation from libui that prevents a programmatic implementation of checking/unchecking a standard checkbox?

AndyObtiva commented 2 years ago

You know what! I was wrong!!! You're right @mohad12211 , there is no limitation. Thank you for indirectly alerting me to that. I got confused when I originally saw the issue and thought it was caused by a limitation because checkboxes inside tables are a bit limited, but they are not related to the reported use case, which is asking about a standard checkbox.

@laurabernardy , your request does have a clean solution apparently. You just call checkbox.checked = false to uncheck a checkbox. That worked on my x86_64 Mac Catalina, ARM64 Mac Monterey, x86_64 Ubuntu Linux, and x86_64 Windows 10. If that is not working for you, please report to me what operating system you are running and what CPU architecture you have (x86_64 or ARM64/AARCH64?).

Here is a modified Control Gallery example whereby checking/unchecking the check menu item in the menu bar affects the checkbox in the GUI programmatically and checking/unchecking the checkbox affects the check menu item programmatically:

Screen Shot 2022-05-18 at 7 29 28 PM

Screen Shot 2022-05-18 at 7 29 35 PM

Version 1 (without data-binding)

# frozen_string_literal: true

require 'glimmer-dsl-libui'

include Glimmer

menu('File') {
  menu_item('Open') {
    on_clicked do
      file = open_file
      puts file unless file.nil?
      $stdout.flush # for Windows
    end
  }

  menu_item('Save') {
    on_clicked do
      file = save_file
      puts file unless file.nil?
      $stdout.flush # for Windows
    end
  }

  quit_menu_item {
    on_clicked do
      puts 'Bye Bye'
    end
  }

  preferences_menu_item # Can optionally contain an on_clicked listener
}

menu('Edit') {
  @check_menu_item = check_menu_item('Checkable Item_') {
    on_clicked do |cmi|
      checked = cmi.checked?
      @checkbox.checked = checked
    end
  }
  separator_menu_item
  menu_item('Disabled Item_') {
    enabled false
  }
}

menu('Help') {
  menu_item('Help')

  about_menu_item # Can optionally contain an on_clicked listener
}

MAIN_WINDOW = window('Control Gallery', 600, 500) {
  margined true

  on_closing do
    puts 'Bye Bye'
  end

  vertical_box {
    horizontal_box {
      group('Basic Controls') {
        vertical_box {
          button('Button') {
            stretchy false

            on_clicked do
              msg_box('Information', 'You clicked the button')
            end
          }

          @checkbox = checkbox('Checkbox') {
            stretchy false

            on_toggled do |c|
              checked = c.checked?
              MAIN_WINDOW.title = "Checkbox is #{checked}"
              c.text = "I am the checkbox (#{checked})"
              @check_menu_item.checked = checked
            end
          }

          label('Label') { stretchy false }

          horizontal_separator { stretchy false }

          date_picker { stretchy false }

          time_picker { stretchy false }

          date_time_picker { stretchy false }

          font_button { stretchy false }

          color_button { stretchy false }
        }
      }

      vertical_box {
        group('Numbers') {
          stretchy false

          vertical_box {
            spinbox(0, 100) {
              stretchy false
              value 42

              on_changed do |s|
                puts "New Spinbox value: #{s.value}"
                $stdout.flush # for Windows
              end
            }

            slider(0, 100) {
              stretchy false

              on_changed do |s|
                v = s.value
                puts "New Slider value: #{v}"
                $stdout.flush # for Windows
                @progress_bar.value = v
              end
            }

            @progress_bar = progress_bar { stretchy false }
          }
        }

        group('Lists') {
          stretchy false

          vertical_box {
            combobox {
              stretchy false
              items 'combobox Item 1', 'combobox Item 2', 'combobox Item 3' # also accepts a single array argument

              on_selected do |c|
                puts "New combobox selection: #{c.selected}"
                $stdout.flush # for Windows
              end
            }

            editable_combobox {
              stretchy false
              items 'Editable Item 1', 'Editable Item 2', 'Editable Item 3' # also accepts a single array argument
            }

            radio_buttons {
              items 'Radio Button 1', 'Radio Button 2', 'Radio Button 3' # also accepts a single array argument
            }
          }
        }

        tab {
          tab_item('Page 1') {
            horizontal_box {
              entry {
                text 'Please enter your feelings'

                on_changed do |e|
                  puts "Current textbox data: '#{e.text}'"
                  $stdout.flush # for Windows
                end
              }
            }
          }

          tab_item('Page 2') {
            horizontal_box
          }

          tab_item('Page 3') {
            horizontal_box
          }
        }
      }
    }
  }
}

MAIN_WINDOW.show

Version 2 (with data-binding)

And, here is a bidirectional data-binding version (via checked <=> [@person, :adult]) of the same Control Gallery sample that synchronizes the check menu item in the menu bar with the checkbox in the GUI:

# frozen_string_literal: true

require 'glimmer-dsl-libui'

include Glimmer

Person = Struct.new(:adult)
@person = Person.new

menu('File') {
  menu_item('Open') {
    on_clicked do
      file = open_file
      puts file unless file.nil?
      $stdout.flush # for Windows
    end
  }

  menu_item('Save') {
    on_clicked do
      file = save_file
      puts file unless file.nil?
      $stdout.flush # for Windows
    end
  }

  quit_menu_item {
    on_clicked do
      puts 'Bye Bye'
    end
  }

  preferences_menu_item # Can optionally contain an on_clicked listener
}

menu('Edit') {
  @check_menu_item = check_menu_item('Checkable Item_') {
    checked <=> [@person, :adult]
  }
  separator_menu_item
  menu_item('Disabled Item_') {
    enabled false
  }
}

menu('Help') {
  menu_item('Help')

  about_menu_item # Can optionally contain an on_clicked listener
}

MAIN_WINDOW = window('Control Gallery', 600, 500) {
  margined true

  on_closing do
    puts 'Bye Bye'
  end

  vertical_box {
    horizontal_box {
      group('Basic Controls') {
        vertical_box {
          button('Button') {
            stretchy false

            on_clicked do
              msg_box('Information', 'You clicked the button')
            end
          }

          @checkbox = checkbox('Checkbox') {
            stretchy false
            checked <=> [@person, :adult]

            on_toggled do |c|
              checked = c.checked?
              MAIN_WINDOW.title = "Checkbox is #{checked}"
              c.text = "I am the checkbox (#{checked})"
            end
          }

          label('Label') { stretchy false }

          horizontal_separator { stretchy false }

          date_picker { stretchy false }

          time_picker { stretchy false }

          date_time_picker { stretchy false }

          font_button { stretchy false }

          color_button { stretchy false }
        }
      }

      vertical_box {
        group('Numbers') {
          stretchy false

          vertical_box {
            spinbox(0, 100) {
              stretchy false
              value 42

              on_changed do |s|
                puts "New Spinbox value: #{s.value}"
                $stdout.flush # for Windows
              end
            }

            slider(0, 100) {
              stretchy false

              on_changed do |s|
                v = s.value
                puts "New Slider value: #{v}"
                $stdout.flush # for Windows
                @progress_bar.value = v
              end
            }

            @progress_bar = progress_bar { stretchy false }
          }
        }

        group('Lists') {
          stretchy false

          vertical_box {
            combobox {
              stretchy false
              items 'combobox Item 1', 'combobox Item 2', 'combobox Item 3' # also accepts a single array argument

              on_selected do |c|
                puts "New combobox selection: #{c.selected}"
                $stdout.flush # for Windows
              end
            }

            editable_combobox {
              stretchy false
              items 'Editable Item 1', 'Editable Item 2', 'Editable Item 3' # also accepts a single array argument
            }

            radio_buttons {
              items 'Radio Button 1', 'Radio Button 2', 'Radio Button 3' # also accepts a single array argument
            }
          }
        }

        tab {
          tab_item('Page 1') {
            horizontal_box {
              entry {
                text 'Please enter your feelings'

                on_changed do |e|
                  puts "Current textbox data: '#{e.text}'"
                  $stdout.flush # for Windows
                end
              }
            }
          }

          tab_item('Page 2') {
            horizontal_box
          }

          tab_item('Page 3') {
            horizontal_box
          }
        }
      }
    }
  }
}

MAIN_WINDOW.show

Cheers and sorry about not answering correctly before (I must have been swamped at the time)!