Files
cyberarm_engine/lib/cyberarm_engine/ui/container.rb

195 lines
5.3 KiB
Ruby

module CyberarmEngine
class Container
include Common
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
attr_reader :origin_x, :origin_y, :origin_width, :origin_height
def initialize(options = {}, block = nil)
@parent = options[:parent] || nil
options = {
x: 0, y: 0, z: 0,
width: 0, height: 0
}.merge(options)
x = options.dig(:x)
y = options.dig(:y)
z = options.dig(:z)
width = options.dig(:width)
height = options.dig(:height)
raise "#{self.class} 'x' must be a number" unless x.is_a?(Numeric)
raise "#{self.class} 'y' must be a number" unless y.is_a?(Numeric)
raise "#{self.class} 'z' must be a number" unless z.is_a?(Numeric)
raise "#{self.class} 'width' must be a number" unless width.is_a?(Numeric)
raise "#{self.class} 'height' must be a number" unless height.is_a?(Numeric)
raise "#{self.class} 'options' must be a Hash" unless options.is_a?(Hash)
@x, @y, @z, @width, @height, @internal_width, @internal_height = x, y, z, width-x, height-y, width-x, height-y
@origin_x, @origin_x = @x, @x
@origin_width, @origin_height = @width, @height
@scroll_x, @scroll_y = 0, 0
@scroll_speed = 10
@block = block
@options = options
@text_color = options[:text_color] || Element::THEME[:stroke]
@background_color = Element::THEME[:background]
@elements = []
@children = []
@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
background
Gosu.translate(@scroll_x, @scroll_y) do
@elements.each(&:draw)
end
end
end
def update
@elements.each(&:update)
end
def button_up(id)
if $window.mouse_x.between?(@x, @x + @width)
if $window.mouse_y.between?(@y, @y + @height)
case id
when Gosu::MsWheelUp
@scroll_y += @scroll_speed
@scroll_y = 0 if @scroll_y > 0
when Gosu::MsWheelDown
@scroll_y -= @scroll_speed
if $window.height - @internal_height - @y > 0
@scroll_y = 0
else
@scroll_y = @height - @internal_height if @scroll_y <= @height - @internal_height
end
end
end
end
@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
# 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 = @origin_width
@height= @origin_height
if @parent
neighbors = @parent.children.size > 0 ? @parent.children.size : 1
@width = @parent.width / neighbors
@height = @parent.height / neighbors
end
widest_element = nil
last_element = nil
@elements.each do |element|
flow(element) if @mode == :flow
stack(element) if @mode == :stack
if element.is_a?(Element)
widest_element ||= element
highest_element ||= element
widest_element = element if element.width > widest_element.width
last_element = element
end
margin = 0
margin = element.margin if defined?(element.margin)
case @mode
when :flow
@internal_width += element.width + margin unless @origin_width.nonzero?
@internal_height = element.height + margin if element.height + margin > @internal_height + margin unless @origin_height.nonzero?
when :stack
@internal_width = element.width + margin if element.width + margin > @internal_width + margin unless @origin_width.nonzero?
@internal_height += element.height + margin unless @origin_height.nonzero?
end
end
@internal_width += widest_element.margin if widest_element && !@origin_width.nonzero?
@internal_height += last_element.margin if last_element && !@origin_height.nonzero?
end
def flow(element)
element.x = @packing_x
if element.is_a?(Container)
element.y = @y
else
element.y = 0
end
element.recalculate
@packing_x += element.margin if defined?(element.margin)
@packing_x += element.width
end
def stack(element)
if element.is_a?(Container)
element.x = @x
else
element.x = 0
end
element.y = @packing_y
element.recalculate
@packing_y += element.margin if defined?(element.margin)
@packing_y += element.height
end
end
end