diff --git a/lib/cyberarm_engine/game_state.rb b/lib/cyberarm_engine/game_state.rb index a122266..c2e6179 100644 --- a/lib/cyberarm_engine/game_state.rb +++ b/lib/cyberarm_engine/game_state.rb @@ -4,15 +4,16 @@ module CyberarmEngine include DSL attr_accessor :options, :global_pause, :active_container, :active_grid - attr_reader :game_objects + attr_reader :game_objects, :containers def initialize(options={}) @options = options @game_objects = [] @global_pause = false - @active_container = nil - @active_grid = nil + @root_container = Stack.new(x: 0, y: 0, width: $window.width, height: $window.height) + @game_objects << @root_container + @containers = [@root_container] setup end diff --git a/lib/cyberarm_engine/ui/button.rb b/lib/cyberarm_engine/ui/button.rb index 8fd261a..4d4eb8d 100644 --- a/lib/cyberarm_engine/ui/button.rb +++ b/lib/cyberarm_engine/ui/button.rb @@ -1,48 +1,44 @@ module CyberarmEngine - class Button < Element - def initialize(text, options = {}, block = nil) - super(options, block) - - @text = Text.new(text, font: @options[:font], color: @options[:interactive_stroke], size: @options[:text_size], shadow: @options[:text_shadow]) - - return self - end - + class Button < Label def draw @text.draw - $window.draw_rect(@x, @y, width, height, @options[:background], @z+1) + $window.draw_rect(relative_x, relative_y, width, height, @options[:background], @z+1) if mouse_over? && $window.button_down?(Gosu::MsLeft) - $window.draw_rect(@x+1, @y+1, width-2, height-2, @options[:interactive_active_background], @z+2) + $window.draw_rect( + relative_x+@options[:interactive_border_size], + relative_y+@options[:interactive_border_size], + width-(@options[:interactive_border_size]*2), + height-(@options[:interactive_border_size]*2), + @options[:interactive_active_background], + @z+2 + ) + + @text.color = @options[:interactive_active_stroke] elsif mouse_over? - $window.draw_rect(@x+1, @y+1, width-2, height-2, @options[:interactive_hover_background], @z+2) + $window.draw_rect( + relative_x+@options[:interactive_border_size], + relative_y+@options[:interactive_border_size], + width-(@options[:interactive_border_size]*2), + height-(@options[:interactive_border_size]*2), + @options[:interactive_hover_background], + @z+2 + ) # show_tooltip + @text.color = @options[:interactive_stroke] else - $window.draw_rect(@x+1, @y+1, width-2, height-2, @options[:interactive_background], @z+2) + $window.draw_rect( + relative_x+@options[:interactive_border_size], + relative_y+@options[:interactive_border_size], + width-(@options[:interactive_border_size]*2), + height-(@options[:interactive_border_size]*2), + @options[:interactive_background], + @z+2 + ) + + @text.color = @options[:interactive_stroke] end end - - def button_up(id) - case id - when Gosu::MsLeft - if mouse_over? - @block.call(self) if @block - end - end - end - - def recalculate - @width = @text.width - @height= @text.height - - @text.x = @x + @padding - @text.y = @y + @padding - @text.z = @z + 3 - end - - def value - @text.text - end end end \ No newline at end of file diff --git a/lib/cyberarm_engine/ui/check_box.rb b/lib/cyberarm_engine/ui/check_box.rb index 256b418..3edc579 100644 --- a/lib/cyberarm_engine/ui/check_box.rb +++ b/lib/cyberarm_engine/ui/check_box.rb @@ -1,36 +1,25 @@ module CyberarmEngine - class CheckBox < Element + class CheckBox < Button def initialize(options, block = nil) - super(options, block) + super(options[:checkmark], options, block) @checked = options[:checked] || false - - @text = Text.new("X", font: @options[:font], color: @options[:interactive_stroke], size: @options[:text_size], shadow: @options[:text_shadow]) - - return self - end - - def draw - $window.draw_rect(@x, @y, width, height, @options[:background], @z+1) - - if mouse_over? - $window.draw_rect(@x+1, @y+1, width-2, height-2, @options[:interactive_hover_background], @z+2) + if @checked + @text.text = options[:checkmark] else - if @checked - $window.draw_rect(@x+1, @y+1, width-2, height-2, @options[:interactive_active_background], @z+2) - else - $window.draw_rect(@x+1, @y+1, width-2, height-2, @options[:interactive_background], @z + 2) - end + @text.text = "" end - @text.draw if @checked + return self end def button_up(id) if mouse_over? && id == Gosu::MsLeft if @checked @checked = false + @text.text = "" else @checked = true + @text.text = @options[:checkmark] end @block.call(self) if @block @@ -38,12 +27,9 @@ module CyberarmEngine end def recalculate - @width = @text.width - @height= @text.height + super - @text.x = @x + @padding - @text.y = @y + @padding - @text.z = @z + 3 + @width = @text.textobject.text_width(@options[:checkmark]) end def value diff --git a/lib/cyberarm_engine/ui/container.rb b/lib/cyberarm_engine/ui/container.rb index c19b606..ee5c03d 100644 --- a/lib/cyberarm_engine/ui/container.rb +++ b/lib/cyberarm_engine/ui/container.rb @@ -2,17 +2,15 @@ module CyberarmEngine class Container include Common - attr_accessor :stroke_color, :fill_color, :background_color - attr_reader :elements, :x, :y, :z, :width, :height, :options + attr_accessor :stroke_color, :fill_color, :background_color, :x, :y, :z, :width, :height + attr_reader :elements, :children, :options attr_reader :scroll_x, :scroll_y, :internal_width, :internal_height def initialize(options = {}, block = nil) - options[:parent].active_container = self - options = { x: 0, y: 0, z: 0, - width: $window.width, height: $window.height - }.merge!(options) + width: 0, height: 0 + }.merge(options) x = options.dig(:x) y = options.dig(:y) @@ -32,23 +30,43 @@ module CyberarmEngine @scroll_x, @scroll_y = 0, 0 @scroll_speed = 10 + @block = block @options = options + @parent = options[:parent] || nil @text_color = options[:text_color] || Gosu::Color::WHITE @background_color = Gosu::Color::NONE + @elements = [] + @children = [] - block.call(self) if block - - options[:parent].active_container = nil - - recalculate + @theme = {} return self end + def build + @block.call(self) if @block + + recalculate + end + + def add_child(container) + @children << container + @elements << container + + recalculate + end + + def add(element) + @elements << element + + recalculate + end + def draw - Gosu.clip_to(x, y, width, height) do + Gosu.clip_to(@x, @y, @width, @height) do + raise "width and height are 0!" if @width == 0 && @height == 0 && Gosu.milliseconds > 1500 && @elements.size > 0 background Gosu.translate(scroll_x, scroll_y) do @@ -68,7 +86,6 @@ module CyberarmEngine when Gosu::MsWheelUp @scroll_y+=@scroll_speed @scroll_y = 0 if @scroll_y > 0 - @elements.each {|e| e.set_offset(@scroll_x, @scroll_y) if e.is_a?(Button) } when Gosu::MsWheelDown @scroll_y-=@scroll_speed if $window.height-@internal_height-y > 0 @@ -76,8 +93,6 @@ module CyberarmEngine else @scroll_y = @height-@internal_height if @scroll_y <= @height-@internal_height end - - @elements.each {|e| e.set_offset(@scroll_x, @scroll_y) if e.is_a?(Button) } end end end @@ -85,32 +100,58 @@ module CyberarmEngine @elements.each {|e| if defined?(e.button_up); e.button_up(id); end} end + def theme + @theme + end + + def stroke(color) + @theme[:stroke] = color + end + + def fill(color) + @theme[:fill] = color + end + def background Gosu.draw_rect(@x, @y, @width, @height, @background_color, @z) end def recalculate raise "mode was not defined!" unless @mode - @packing_x = @x - @packing_y = @y + # puts "<#{self.class}:#{self.object_id}> X: #{@x}, Y: #{@y}, width: #{@width}, height: #{@height} (children: #{@children.count}, parents: #{@parent&.children&.count})" + + @packing_x = 0 + @packing_y = 0 + + @width = 0 + @height= 0 @elements.each do |element| flow(element) if @mode == :flow stack(element) if @mode == :stack + + case @mode + when :flow + @width += element.width + @height = element.height if element.height > @height + when :stack + @height += element.height + @width = element.width if element.width > @width + end end end def flow(element) - element.x = @x + @packing_x - element.y = @y + element.x = @packing_x + element.y = 0 element.recalculate @packing_x += element.width + 1 end def stack(element) - element.x = @x - element.y = @y + @packing_y + element.x = 0 + element.y = @packing_y element.recalculate @packing_y += element.height + 1 diff --git a/lib/cyberarm_engine/ui/dsl.rb b/lib/cyberarm_engine/ui/dsl.rb index b3648e1..a018536 100644 --- a/lib/cyberarm_engine/ui/dsl.rb +++ b/lib/cyberarm_engine/ui/dsl.rb @@ -2,60 +2,72 @@ module CyberarmEngine module DSL def flow(options = {}, &block) puts "Flow" - options[:parent] = self - _flow = Flow.new(options, block) + options[:parent] = @containers.last + _container = Flow.new(options, block) + @containers << _container + _container.build + options[:parent].add_child(_container) + @containers.pop - @active_container = _flow - @game_objects << _flow - - return _flow + return _container end def stack(options = {}, &block) puts "Stack" - options[:parent] = self - _stack = Stack.new(options, block) + options[:parent] = @containers.last + _container = Stack.new(options, block) + @containers << _container + _container.build + options[:parent].add_child(_container) + @containers.pop - @active_container = _stack - @game_objects << _stack - - return _stack + return _container end - def label(text, options = {}) - options[:parent] = @active_container - _text = Label.new(text, options) - @active_container.elements << _text + def label(text, options = {}, &block) + options[:parent] = @containers.last + _element = Label.new(text, options, block) + @containers.last.add(_element) - return _text + return _element end def button(text, options = {}, &block) - options[:parent] = @active_container - _button = Button.new(text, options, block) { if block.is_a?(Proc); block.call; end } - @active_container.elements << _button + options[:parent] = @containers.last + _element = Button.new(text, options, block) { if block.is_a?(Proc); block.call; end } + @containers.last.add(_element) - return _button + return _element end def edit_line(text, options = {}, &block) - options[:parent] = @active_container - _edit_line = EditLine.new(text, options, block) - @active_container.elements << _edit_line + options[:parent] = @containers.last + _element = EditLine.new(text, options, block) + @containers.last.add(_element) - return _edit_line + return _element end def check_box(options = {}, &block) - options[:parent] = @active_container - _check_box = CheckBox.new(options, block) - @active_container.elements << _check_box + options[:parent] = @containers.last + _element = CheckBox.new(options, block) + @containers.last.add(_element) - return _check_box + return _element end def background(color = Gosu::Color::NONE) - @active_container.background_color = color + @containers.last.background_color = color + end + + # Foreground color, e.g. Text + def stroke(color) + @containers.last.stroke(color) + end + + # Element background color + def fill(color) + @containers.last.fill(color) end end end \ No newline at end of file diff --git a/lib/cyberarm_engine/ui/element.rb b/lib/cyberarm_engine/ui/element.rb index df200f8..d9f3b06 100644 --- a/lib/cyberarm_engine/ui/element.rb +++ b/lib/cyberarm_engine/ui/element.rb @@ -13,33 +13,32 @@ module CyberarmEngine stroke: Gosu::Color::WHITE, fill: Gosu::Color::NONE, background: Gosu::Color.rgb(12,12,12), + checkmark: "X", # ✓ padding: 20, margin: 0, interactive_stroke: Gosu::Color::WHITE, - interactive_active_stroke: Gosu::Color::BLACK, + interactive_active_stroke: Gosu::Color::GRAY, interactive_background: Gosu::Color::GRAY, interactive_hover_background: Gosu::Color.rgb(100, 100, 100), interactive_active_background: Gosu::Color.rgb(50, 50, 50), + interactive_border_size: 1, text_size: 22, text_shadow: true, font: "Consolas" } - attr_accessor :x, :y, :z - attr_accessor :offset_x, :offset_y + attr_accessor :x, :y, :z, :width, :height, :padding, :margin def initialize(options = {}, block = nil) - options = (THEME).merge(DEFAULTS).merge(options) + @parent = options[:parent] # parent Container (i.e. flow/stack) + options = (THEME).merge(DEFAULTS).merge(@parent.theme).merge(options) @options = options @block = block - @offset_x = 0 - @offset_y = 0 - @x = options[:x] @y = options[:y] @z = options[:z] @@ -49,8 +48,6 @@ module CyberarmEngine @padding = options[:padding] @margin = options[:margin] - - @parent = options[:parent] end def draw @@ -66,8 +63,8 @@ module CyberarmEngine end def mouse_over? - if $window.mouse_x.between?(@x + @offset_x, @x + @offset_x + width) - if $window.mouse_y.between?(@y + @offset_y, @y + @offset_y + height) + if $window.mouse_x.between?(relative_x, relative_x + width) + if $window.mouse_y.between?(relative_y, relative_y + height) true end end @@ -81,11 +78,20 @@ module CyberarmEngine @height + (@padding * 2) end + def relative_x + @parent.x + @parent.scroll_x + @x + end + + def relative_y + @parent.y + @parent.scroll_y + @y + end + def recalculate + raise "#{self.class}#recalculate was not overridden!" end def value - raise "#{self.klass}#value was not overridden!" + raise "#{self.class}#value was not overridden!" end end end \ No newline at end of file diff --git a/lib/cyberarm_engine/ui/label.rb b/lib/cyberarm_engine/ui/label.rb index 65f43a9..1fc128c 100644 --- a/lib/cyberarm_engine/ui/label.rb +++ b/lib/cyberarm_engine/ui/label.rb @@ -9,18 +9,31 @@ module CyberarmEngine end def draw - $window.draw_rect(@x, @y, width, height, @options[:fill], @z+1) + $window.draw_rect(relative_x, relative_y, width, height, @options[:fill], @z+1) @text.draw end + def button_up(id) + case id + when Gosu::MsLeft + if mouse_over? + @block.call(self) if @block + end + end + end + def recalculate @width = @text.width @height= @text.height - @text.x = @x + @padding - @text.y = @y + @padding + @text.x = relative_x + @padding + @text.y = relative_y + @padding @text.z = @z + 3 end + + def value + @text.text + end end end \ No newline at end of file