AndyObtiva/glimmer-dsl-libui

View on GitHub
lib/glimmer/libui/control_proxy/window_proxy.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# Copyright (c) 2021-2024 Andy Maleh
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

require 'glimmer/libui/control_proxy'
require 'glimmer/libui/control_proxy/area_proxy'

module Glimmer
  module LibUI
    class ControlProxy
      # Proxy for LibUI window objects
      #
      # Follows the Proxy Design Pattern
      class WindowProxy < ControlProxy
        CUSTOM_LISTENER_NAMES = ['on_destroy']
        CUSTOM_LISTENER_NAME_ALIASES = {
          on_destroyed: 'on_destroy',
        }
        DEFAULT_TITLE = ''
        DEFAULT_WIDTH = 190
        DEFAULT_HEIGHT = 150
        DEFAULT_HAS_MENUBAR = 1
        
        def post_initialize_child(child)
          ::LibUI.window_set_child(@libui, child.libui)
        end
        
        def destroy_child(child)
          ::LibUI.send("window_set_child", @libui, nil)
          super
        end
        
        def destroy
          return if @destroying
          @destroying = true
          @on_closing_listeners&.clear
          super
          ControlProxy.image_proxies.each { |image_proxy| ::LibUI.free_image(image_proxy.libui) unless image_proxy.area_image? }
          notify_custom_listeners('on_destroy', self)
          deregister_custom_listeners('on_destroy')
          @destroying = false
        end
        
        def destroying?
          @destroying
        end
        
        def show
          super
          unless @shown_at_least_once
            @shown_at_least_once = true
            ::LibUI.main
            ::LibUI.quit
          end
        end
        alias open show
        
        def handle_listener(listener_name, &listener)
          case listener_name
          when 'on_closing'
            @on_closing_listeners ||= []
            @on_closing_listeners << listener
            @default_behavior_listener ||= Proc.new do
              return_value = nil
              @on_closing_listeners.each do |l|
                return_value = l.call(self)
                break if return_value.is_a?(Numeric) || return_value.is_a?(TrueClass) || return_value.is_a?(FalseClass)
              end
              if return_value.is_a?(Numeric)
                return_value
              elsif return_value.is_a?(TrueClass) || return_value.is_a?(FalseClass)
                return_value ? 1 : 0
              elsif self != ControlProxy.main_window_proxy
                1
              else
                destroy
                ::LibUI.quit
                0
              end
            end.tap do |default_behavior_listener|
              super(listener_name, &default_behavior_listener)
            end
          else
            super
          end
        end
        
        def content_size(*args)
          if args.empty?
            width = Fiddle::Pointer.malloc(64)
            height = Fiddle::Pointer.malloc(64)
            ::LibUI.window_content_size(@libui, width, height)
            width = width[0, 64].unpack1('i')
            height = height[0, 64].unpack1('i')
            [width, height]
          else
            args = args.first if args.size == 1 && args.first.is_a?(Array)
            super
            @width = args[0]
            @height = args[1]
          end
        end
        alias content_size= content_size
        alias set_content_size content_size
        
        def width(value = nil)
          if value.nil?
            content_size.first
          else
            set_content_size(value, height)
          end
        end
        alias width= width
        alias set_width width
        
        def height(value = nil)
          if value.nil?
            content_size.last
          else
            set_content_size(width, value)
          end
        end
        alias height= height
        alias set_height height
        
        def resizable(value = nil)
          if value.nil?
            @resizable = true if @resizable.nil?
            @resizable
          else
            @resizable = value
            if !@resizable && !@resizable_listener_registered
              handle_listener('on_content_size_changed') do
                set_content_size(@width, @height) unless @resizable
              end
              @resizable_listener_registered = true
            end
          end
        end
        alias resizable? resizable
        alias resizable= resizable
        alias set_resizable resizable
        
        def focused
          Glimmer::LibUI.integer_to_boolean(super)
        end
        alias focused? focused
      
        private
        
        def build_control
          if OS.mac? && ControlProxy.menu_proxies.empty?
            menu_proxy = ControlProxy.create('menu', nil, [''])
            quit_menu_item_proxy = ControlProxy.create('quit_menu_item', menu_proxy, [])
          end
          construction_args = @args.dup
          construction_args[0] = DEFAULT_TITLE if construction_args.size == 0
          construction_args[1] = DEFAULT_WIDTH if construction_args.size == 1
          construction_args[2] = DEFAULT_HEIGHT if construction_args.size == 2
          construction_args[3] = DEFAULT_HAS_MENUBAR if construction_args.size == 3
          construction_args[3] = Glimmer::LibUI.boolean_to_integer(construction_args[3]) if construction_args.size == 4 && (construction_args[3].is_a?(TrueClass) || construction_args[3].is_a?(FalseClass))
          @width = construction_args[1]
          @height = construction_args[2]
          @libui = ControlProxy.new_control(@keyword, construction_args)
          @libui.tap do
            # setup default on_closing listener if no on_closing listeners are setup
            handle_listener('on_closing') {} if @on_closing_listeners.nil? || @on_closing_listeners.empty?
          end
        end
      end
    end
  end
end