diff --git a/cyberarm_engine.gemspec b/cyberarm_engine.gemspec index 1ff25fc..2170558 100644 --- a/cyberarm_engine.gemspec +++ b/cyberarm_engine.gemspec @@ -27,9 +27,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = %w[lib assets] - spec.add_dependency "excon", "~> 0.88" spec.add_dependency "gosu", "~> 1.1" - spec.add_dependency "gosu_more_drawables", "~> 0.3" # spec.add_dependency "ffi", :platforms => [:mswin, :mingw] # Required by Clipboard on Windows spec.add_development_dependency "bundler", "~> 2.2" diff --git a/lib/cyberarm_engine.rb b/lib/cyberarm_engine.rb index 6522519..b5e63d6 100644 --- a/lib/cyberarm_engine.rb +++ b/lib/cyberarm_engine.rb @@ -6,7 +6,6 @@ else require "gosu" end require "json" -require "excon" require_relative "cyberarm_engine/version" require_relative "cyberarm_engine/stats" @@ -62,6 +61,8 @@ require_relative "cyberarm_engine/ui/elements/check_box" require_relative "cyberarm_engine/ui/elements/radio" require_relative "cyberarm_engine/ui/elements/progress" require_relative "cyberarm_engine/ui/elements/slider" +require_relative "cyberarm_engine/ui/elements/menu" +require_relative "cyberarm_engine/ui/elements/menu_item" require_relative "cyberarm_engine/game_state" require_relative "cyberarm_engine/ui/gui_state" diff --git a/lib/cyberarm_engine/cache/download_manager.rb b/lib/cyberarm_engine/cache/download_manager.rb index 031d3bf..2cd680f 100644 --- a/lib/cyberarm_engine/cache/download_manager.rb +++ b/lib/cyberarm_engine/cache/download_manager.rb @@ -1,3 +1,5 @@ +require "excon" + module CyberarmEngine module Cache class DownloadManager diff --git a/lib/cyberarm_engine/stats.rb b/lib/cyberarm_engine/stats.rb index 6c3091b..52a3504 100644 --- a/lib/cyberarm_engine/stats.rb +++ b/lib/cyberarm_engine/stats.rb @@ -146,10 +146,15 @@ module CyberarmEngine slice += 1 end + max_node = CyberarmEngine::Stats.frames.select(&:complete?).map { |f| f.frame_timing.duration }.max + scale = 1 + scale = (@height - @padding).to_f / max_node + scale = 1 if scale > 1 + nodes.each_with_index do |cluster, i| break if cluster.empty? - @graphs[:frame_timings] << CyberarmEngine::Vector.new(@position.x + @padding + 1 * i, (@position.y + @height - @padding) - cluster.max) + @graphs[:frame_timings] << CyberarmEngine::Vector.new(@position.x + @padding + 1 * i, (@position.y + @height - @padding) - cluster.max * scale) end end @@ -187,6 +192,8 @@ module CyberarmEngine "TIMINGS:\n#{frame.attempted_multitiming? ? "Attempted Multitiming!\nTimings may be inaccurate for:\n#{frame.multitimings.map { |m, _| m}.join("\n") }\n\n" : ''}#{frame.timings.map { |t, v| "#{t}: #{v.duration}ms" }.join("\n")}" Gosu.draw_rect(@data_label.x - @padding, @data_label.y - @padding, @data_label.width + @padding * 2, @data_label.height + @padding * 2, 0xdd_222222, @position.z) @data_label.draw + + # puts "Recalcs this frame: #{frame.counters[:gui_recalculations]} [dt: #{(CyberarmEngine::Window.dt * 1000).round} ms]" if frame.counters[:gui_recalculations] && frame.counters[:gui_recalculations].positive? end end end diff --git a/lib/cyberarm_engine/ui/dsl.rb b/lib/cyberarm_engine/ui/dsl.rb index 8e3a5ca..8d2b1ba 100644 --- a/lib/cyberarm_engine/ui/dsl.rb +++ b/lib/cyberarm_engine/ui/dsl.rb @@ -8,12 +8,8 @@ module CyberarmEngine container(CyberarmEngine::Element::Stack, options, &block) end - # TODO: Remove in version 0.16.0+ - def label(text, options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme - - add_element(Element::TextBlock.new(text, options, block)) + def menu(options = {}, &block) + container(CyberarmEngine::Element::Menu, options, &block) end [ @@ -27,72 +23,79 @@ module CyberarmEngine "Link" ].each do |const| define_method(:"#{const.downcase}") do |text, options = {}, &block| - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element.const_get(const).new(text, options, block)) end end def button(text, options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::Button.new(text, options, block) { block.call if block.is_a?(Proc) }) end def list_box(options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::ListBox.new(options, block) { block.call if block.is_a?(Proc) }) end + def menu_item(text, options = {}, &block) + options[:parent] ||= element_parent + options[:theme] ||= current_theme + + add_element(Element::MenuItem.new(text, options, block) { block.call if block.is_a?(Proc) }) + end + def edit_line(text, options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::EditLine.new(text, options, block)) end def edit_box(text, options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::EditBox.new(text, options, block)) end def toggle_button(options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::ToggleButton.new(options, block)) end def check_box(text, options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::CheckBox.new(text, options, block)) end def image(path, options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::Image.new(path, options, block)) end def progress(options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::Progress.new(options, block)) end def slider(options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme add_element(Element::Slider.new(options, block)) end @@ -102,7 +105,7 @@ module CyberarmEngine end def theme(theme) - element_parent.options[:theme] = theme + element_parent.options[:theme] ||= theme end def current_theme @@ -120,8 +123,8 @@ module CyberarmEngine end private def container(klass, options = {}, &block) - options[:parent] = element_parent - options[:theme] = current_theme + options[:parent] ||= element_parent + options[:theme] ||= current_theme _container = klass.new(options, block) @@ -129,7 +132,7 @@ module CyberarmEngine CyberarmEngine::Element::Container.current_container = _container _container.build - _container.parent.add(_container) + _container.parent.add(_container) unless _container.is_a?(CyberarmEngine::Element::Menu) CyberarmEngine::Element::Container.current_container = old_parent diff --git a/lib/cyberarm_engine/ui/element.rb b/lib/cyberarm_engine/ui/element.rb index 29aaf74..0bc11c4 100644 --- a/lib/cyberarm_engine/ui/element.rb +++ b/lib/cyberarm_engine/ui/element.rb @@ -180,9 +180,7 @@ module CyberarmEngine return if self.is_a?(ToolTip) - if old_width != width || old_height != height - root.gui_state.request_recalculate - end + root.gui_state.request_recalculate if old_width != width || old_height != height stylize end @@ -577,7 +575,7 @@ module CyberarmEngine end def recalculate_if_size_changed - if !is_a?(ToolTip) && (@old_width != width || @old_height != height) + if @parent && !is_a?(ToolTip) && (@old_width != width || @old_height != height) root.gui_state.request_recalculate @old_width = width @@ -628,7 +626,19 @@ module CyberarmEngine end def recalculate - raise "#{self.class}#recalculate was not overridden!" + old_width = width + old_height = height + + stylize + layout + + root.gui_state.request_recalculate if @parent && !is_a?(ToolTip) && (width != old_width || height != old_height) + root.gui_state.request_repaint if width != old_width || height != old_height + + root.gui_state.menu.recalculate if root.gui_state.menu && root.gui_state.menu.parent == self + end + + def layout end def reposition diff --git a/lib/cyberarm_engine/ui/elements/button.rb b/lib/cyberarm_engine/ui/elements/button.rb index a6334b3..66c9255 100644 --- a/lib/cyberarm_engine/ui/elements/button.rb +++ b/lib/cyberarm_engine/ui/elements/button.rb @@ -34,7 +34,7 @@ module CyberarmEngine @text.draw end - def recalculate + def layout unless @enabled @style.background_canvas.background = @style.disabled[:background] @text.color = @style.disabled[:color] diff --git a/lib/cyberarm_engine/ui/elements/container.rb b/lib/cyberarm_engine/ui/elements/container.rb index 7e8093a..ad903da 100644 --- a/lib/cyberarm_engine/ui/elements/container.rb +++ b/lib/cyberarm_engine/ui/elements/container.rb @@ -49,9 +49,7 @@ module CyberarmEngine root.gui_state.request_recalculate_for(self) if @children.delete(element) end - def clear(&block) - @children.clear - + def append(&block) old_container = CyberarmEngine::Element::Container.current_container CyberarmEngine::Element::Container.current_container = self @@ -62,7 +60,9 @@ module CyberarmEngine root.gui_state.request_recalculate_for(self) end - def append(&block) + def clear(&block) + @children.clear + old_container = CyberarmEngine::Element::Container.current_container CyberarmEngine::Element::Container.current_container = self @@ -151,20 +151,24 @@ module CyberarmEngine end def recalculate + return if @in_recalculate + + @in_recalculate = true + @current_position = Vector.new(@style.margin_left + @style.padding_left, @style.margin_top + @style.padding_top) return unless visible? Stats.frame&.increment(:gui_recalculations) - stylize - # s = Gosu.milliseconds + stylize layout - old_width = @width - old_height = @height + # Old sizes MUST be determined AFTER call to layout + old_width = width + old_height = height @cached_scroll_width = nil @cached_scroll_height = nil @@ -207,6 +211,7 @@ module CyberarmEngine end end + # t = Gosu.milliseconds # Move children to parent after positioning @children.each do |child| child.x += (@x + @style.border_thickness_left) - style.margin_left @@ -216,12 +221,11 @@ module CyberarmEngine child.recalculate child.reposition # TODO: Implement top,bottom,left,center, and right positioning - Stats.frame.increment(:gui_recalculations) + Stats.frame&.increment(:gui_recalculations) update_child_element_visibity(child) end - - # puts "TOOK: #{Gosu.milliseconds - s}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}" + # puts "TOOK: #{Gosu.milliseconds - t}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}'s #{@children.count} children" update_background @@ -236,17 +240,11 @@ module CyberarmEngine @scroll_target_position.y = 0 end - # NOTE: Experiment for removing need to explicitly call gui_state#recalculate at least 3 times for layout to layout... - if old_width != @width || old_height != @height - if @parent - root.gui_state.request_recalculate_for(@parent) - else - root.gui_state.request_recalculate - end - end - - root.gui_state.request_repaint if @width != old_width || @height != old_height recalculate_if_size_changed + + # puts "TOOK: #{Gosu.milliseconds - s}ms to recalculate #{self.class}:0x#{self.object_id.to_s(16)}" + + @in_recalculate = false end def layout diff --git a/lib/cyberarm_engine/ui/elements/edit_line.rb b/lib/cyberarm_engine/ui/elements/edit_line.rb index 15bc1ea..cdcb0e3 100644 --- a/lib/cyberarm_engine/ui/elements/edit_line.rb +++ b/lib/cyberarm_engine/ui/elements/edit_line.rb @@ -278,7 +278,7 @@ module CyberarmEngine :handled end - def recalculate + def layout super @width = dimensional_size(@style.width, :width) || default(:width) diff --git a/lib/cyberarm_engine/ui/elements/image.rb b/lib/cyberarm_engine/ui/elements/image.rb index 66137de..a8d06f0 100644 --- a/lib/cyberarm_engine/ui/elements/image.rb +++ b/lib/cyberarm_engine/ui/elements/image.rb @@ -27,7 +27,7 @@ module CyberarmEngine :handled end - def recalculate + def layout _width = dimensional_size(@style.width, :width) _height = dimensional_size(@style.height, :height) diff --git a/lib/cyberarm_engine/ui/elements/list_box.rb b/lib/cyberarm_engine/ui/elements/list_box.rb index c763509..973a358 100644 --- a/lib/cyberarm_engine/ui/elements/list_box.rb +++ b/lib/cyberarm_engine/ui/elements/list_box.rb @@ -12,21 +12,7 @@ module CyberarmEngine @style.background_canvas.background = default(:background) - # TODO: "Clean Up" into own class? - @menu = Stack.new(parent: self, theme: @options[:theme]) - @menu.define_singleton_method(:recalculate_menu) do - @x = @__list_box.x - @y = parent.parent.scroll_top + @__list_box.y + @__list_box.height - - @y = (parent.parent.scroll_top + @__list_box.y) - height if @y + height > window.height - end - @menu.instance_variable_set(:"@__list_box", self) - - def @menu.recalculate - super - - recalculate_menu - end + @menu = Menu.new(parent: self, theme: @options[:theme]) self.choose = @choose end @@ -40,7 +26,13 @@ module CyberarmEngine def choose=(item) valid = @items.detect { |i| i == item } - raise "Invalid value '#{item}' for choose, valid options were: #{@items.map { |i| "#{i.inspect}" }.join(", ")}" unless valid + + unless valid + warn "Invalid value '#{item}' for choose, valid options were: #{@items.map { |i| "#{i.inspect}" }.join(", ")}" + item = @items.first + + raise "No items list" unless item + end @choose = item @@ -62,39 +54,25 @@ module CyberarmEngine end def show_menu - @menu.clear + @menu.clear do - @menu.style.width = width + @menu.style.width = width - @items.each do |item| - next if item == self.value + @items.each do |item| + # prevent already selected item from appearing in list + # NOTE: Remove this? Might be kinda confusing... + next if item == self.value - btn = Button.new( - item, - { - parent: @menu, - width: 1.0, - theme: @options[:theme], - margin: 0, - border_color: 0x00ffffff - }, - proc do + root.gui_state.menu_item(item, width: 1.0, margin: 0, border_color: 0x00ffffff) do self.choose = item @block&.call(self.value) end - ) - - @menu.add(btn) + end end + recalculate - root.gui_state.show_menu(@menu) - end - - def recalculate - super - - @menu.recalculate + @menu.show end end end diff --git a/lib/cyberarm_engine/ui/elements/menu.rb b/lib/cyberarm_engine/ui/elements/menu.rb new file mode 100644 index 0000000..09d0d83 --- /dev/null +++ b/lib/cyberarm_engine/ui/elements/menu.rb @@ -0,0 +1,27 @@ +module CyberarmEngine + class Element + class Menu < Stack + def recalculate + super + + recalculate_menu + end + + def recalculate_menu + # FIXME: properly find scrollable parent, if any. + parent_scroll_top = parent&.parent ? parent.parent.scroll_top : 0 + + @x = @parent.x + @y = parent_scroll_top + @parent.y + @parent.height + + @y = (parent_scroll_top + @parent.y) - height if @y + height > window.height + end + + def show + recalculate + + root.gui_state.show_menu(self) + end + end + end +end diff --git a/lib/cyberarm_engine/ui/elements/menu_item.rb b/lib/cyberarm_engine/ui/elements/menu_item.rb new file mode 100644 index 0000000..164df1d --- /dev/null +++ b/lib/cyberarm_engine/ui/elements/menu_item.rb @@ -0,0 +1,6 @@ +module CyberarmEngine + class Element + class MenuItem < Button + end + end +end diff --git a/lib/cyberarm_engine/ui/elements/progress.rb b/lib/cyberarm_engine/ui/elements/progress.rb index f478224..86a540b 100644 --- a/lib/cyberarm_engine/ui/elements/progress.rb +++ b/lib/cyberarm_engine/ui/elements/progress.rb @@ -19,7 +19,7 @@ module CyberarmEngine @fraction_background.draw end - def recalculate + def layout _width = dimensional_size(@style.width, :width) _height = dimensional_size(@style.height, :height) @width = _width diff --git a/lib/cyberarm_engine/ui/elements/slider.rb b/lib/cyberarm_engine/ui/elements/slider.rb index 1df4669..b2b9f6c 100644 --- a/lib/cyberarm_engine/ui/elements/slider.rb +++ b/lib/cyberarm_engine/ui/elements/slider.rb @@ -47,7 +47,7 @@ module CyberarmEngine add(@handle) end - def recalculate + def layout _width = dimensional_size(@style.width, :width) _height = dimensional_size(@style.height, :height) @@ -55,7 +55,7 @@ module CyberarmEngine @height = _height position_handle - @handle.recalculate + @handle.layout @handle.update_background update_background diff --git a/lib/cyberarm_engine/ui/elements/text_block.rb b/lib/cyberarm_engine/ui/elements/text_block.rb index da9cd3f..07e61ca 100644 --- a/lib/cyberarm_engine/ui/elements/text_block.rb +++ b/lib/cyberarm_engine/ui/elements/text_block.rb @@ -38,16 +38,13 @@ module CyberarmEngine end end - def recalculate + def layout unless @enabled @text.color = @style.disabled[:color] else @text.color = @style.color end - old_width = @width - old_height = @height - @width = 0 @height = 0 @@ -91,9 +88,6 @@ module CyberarmEngine end update_background - - root.gui_state.request_repaint if @width != old_width || @height != old_height - recalculate_if_size_changed end def handle_text_wrapping(max_width) diff --git a/lib/cyberarm_engine/ui/elements/toggle_button.rb b/lib/cyberarm_engine/ui/elements/toggle_button.rb index 4e7220d..4efb3c2 100644 --- a/lib/cyberarm_engine/ui/elements/toggle_button.rb +++ b/lib/cyberarm_engine/ui/elements/toggle_button.rb @@ -7,8 +7,6 @@ module CyberarmEngine if options.dig(:theme, :ToggleButton, :checkmark_image) options[:theme][:ToggleButton][:image_width] ||= options[:theme][:TextBlock][:text_size] super(get_image(options.dig(:theme, :ToggleButton, :checkmark_image)), options, block) - - @_image = @image else super(options[:checkmark], options, block) end @@ -16,10 +14,8 @@ module CyberarmEngine @value = options[:checked] || false if @value - @image = @_image if @_image @raw_text = @options[:checkmark] else - @image = nil @raw_text = "" end end @@ -32,6 +28,14 @@ module CyberarmEngine :handled end + def render + if @image + draw_image if @value + else + draw_text + end + end + def recalculate super return if @image @@ -49,10 +53,8 @@ module CyberarmEngine @value = boolean if boolean - @image = @_image if @_image @raw_text = @options[:checkmark] else - @image = nil @raw_text = "" end diff --git a/lib/cyberarm_engine/ui/gui_state.rb b/lib/cyberarm_engine/ui/gui_state.rb index 1e037fe..45c842a 100644 --- a/lib/cyberarm_engine/ui/gui_state.rb +++ b/lib/cyberarm_engine/ui/gui_state.rb @@ -38,6 +38,10 @@ module CyberarmEngine @tip = Element::ToolTip.new("", parent: @root_container, z: Float::INFINITY, theme: current_theme) end + def menu + @menu + end + # throws :blur event to focused element and sets GuiState focused element # Does NOT throw :focus event at element or set element as focused def focus=(element) @@ -50,6 +54,24 @@ module CyberarmEngine end def draw + Stats.frame.start_timing(:gui_element_recalculate_requests) + + # puts "PENDING REQUESTS: #{@pending_element_recalculate_requests.size}" if @pending_element_recalculate_requests.size.positive? + @pending_element_recalculate_requests.each(&:recalculate) + @pending_element_recalculate_requests.clear + + Stats.frame.end_timing(:gui_element_recalculate_requests) + + if @pending_recalculate_request + Stats.frame.start_timing(:gui_recalculate) + + @root_container.recalculate + + @pending_recalculate_request = false + + Stats.frame.end_timing(:gui_recalculate) + end + super if @menu @@ -78,24 +100,6 @@ module CyberarmEngine end def update - Stats.frame.start_timing(:gui_element_recalculate_requests) - - # puts "PENDING REQUESTS: #{@pending_element_recalculate_requests.size}" if @pending_element_recalculate_requests.size.positive? - @pending_element_recalculate_requests.each(&:recalculate) - @pending_element_recalculate_requests.clear - - Stats.frame.end_timing(:gui_element_recalculate_requests) - - if @pending_recalculate_request - Stats.frame.start_timing(:gui_recalculate) - - @root_container.recalculate - - @pending_recalculate_request = false - - Stats.frame.end_timing(:gui_recalculate) - end - if @pending_focus_request @pending_focus_request = false diff --git a/lib/cyberarm_engine/ui/theme.rb b/lib/cyberarm_engine/ui/theme.rb index e959ce3..13b52d3 100644 --- a/lib/cyberarm_engine/ui/theme.rb +++ b/lib/cyberarm_engine/ui/theme.rb @@ -208,6 +208,17 @@ module CyberarmEngine fraction_background: [0xffc75e61, 0xffe26623], border_thickness: 1, border_color: [0xffd59674, 0xffff8746] + }, + Menu: { # < Stack + width: 200, + border_color: 0xaa_efefef, + border_thickness: 1 + }, + + MenuItem: { # < Button + width: 1.0, + text_left: :left, + margin: 0 } }.freeze end