# Copyright 2022-2024 Frederico de Oliveira Linhares # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. module CandyGear class Menu class Stack def initialize(menu) @stack = []; @stack << menu; end def current_menu() = @stack[-1]; def push(menu) = @stack << menu; def pop() @stack.pop() if @stack.size > 1; end def draw() = @stack.each {_1.draw();} def size() = @stack.size; end class BorderlessView attr_reader(:texture, :font, :text_color, :bg_color, :sprites); def initialize(texture, font, sprite_width, sprite_height) @texture = texture; @font = font; @sprite_width = sprite_width; @sprite_height = sprite_height; @sprites = { arrow_select: Sprite.new( @texture, 1.0/3.0, 1.0/3.0, 2.0/3.0, 2.0/3.0) } end def draw(x, y, width, height) # Nothing. end def border_width() = 0; def border_height() = 0; end class BorderedView attr_reader(:texture, :font, :sprites, :border_width, :border_height); def initialize(texture, font, sprite_width, sprite_height) @texture = texture; @font = font; @border_width = sprite_width; @border_height = sprite_height; @sprites = { arrow_select: Sprite.new( @texture, 1.0/3.0, 1.0/3.0, 2.0/3.0, 2.0/3.0), box_top_left: Sprite.new( @texture, 0.0, 0.0, 1.0/3.0, 1.0/3.0), box_top: Sprite.new( @texture, 1.1/3.0, 0.0, 1.9/3.0, 1.0/3.0), box_top_right: Sprite.new( @texture, 2.0/3.0, 0.0, 1.0, 1.0/3.0), box_left: Sprite.new( @texture, 0.0, 1.1/3.0, 1.0/3.0, 1.9/3.0), box_right: Sprite.new( @texture, 2.0/3.0, 1.1/3.0, 1.0, 1.9/3.0), box_bottom_left: Sprite.new( @texture, 0.0, 2.0/3.0, 1.0/3.0, 1.0), box_bottom: Sprite.new( @texture, 1.1/3.0, 2.0/3.0, 1.9/3.0, 1.0), box_bottom_right: Sprite.new( @texture, 2.0/3.0, 2.0/3.0, 1.0, 1.0) } end def draw(view, x, y, width, height) num_horizontal_sprites = width / @border_width + 1; num_horizontal_sprites += 1 if width % @border_width > 0; num_vertical_sprites = height / @border_height; num_vertical_sprites += 1 if height % @border_height > 0; # Draw the corners. @sprites[:box_top_left].draw(view, x, y, border_width, border_height); @sprites[:box_top_right].draw( view, @border_width * (num_horizontal_sprites + 1) + x, y, border_width, border_height); @sprites[:box_bottom_left].draw( view, x, @border_height * (num_vertical_sprites + 1) + y, border_width, border_height); @sprites[:box_bottom_right].draw( view, @border_width * (num_horizontal_sprites + 1) + x, @border_height * (num_vertical_sprites + 1) + y, border_width, border_height); # Draw the edges. num_horizontal_sprites.times do |i| # Top @sprites[:box_top].draw( view, @border_width * (i + 1) + x, y, border_width, border_height); # Bottom @sprites[:box_bottom].draw( view, @border_width * (i + 1) + x, @border_height * (num_vertical_sprites + 1) + y, border_width, border_height); end num_vertical_sprites.times do |i| # Left @sprites[:box_left].draw( view, x, @border_height * (i + 1) + y, border_width, border_height); # Right @sprites[:box_right].draw( view, @border_width * (num_horizontal_sprites + 1) + x, @border_height * (i + 1) + y, border_width, border_height); end end end class Option attr_reader(:action, :selection, :text, :width, :height); def initialize(text, width, height, action, selection = nil); @text = text; @action = action; @width = width; @height = height; @selection = selection; end end attr_reader(:width, :height); def initialize(view, menu_view, pos_x, pos_y, options) @view = view; @menu_view = menu_view; @pos_x = pos_x; @pos_y = pos_y; @options = options.map do |opt| texture = Texture.from_text(menu_view.font, opt[:text]) Option.new( Sprite.new(texture, 0, 0, 1.0, 1.0), texture.width, texture.height, opt[:action], opt[:selection]); end @current_option = 0; @option_max_width = 0; @option_max_height = 0; @options.each do |i| if @option_max_width < i.width then @option_max_width = i.width; end if @option_max_height < i.height then @option_max_height = i.height; end end @width = @option_max_width; @height = @option_max_height * @options.size; end def next_opt() @current_option += 1; @current_option = 0 if @current_option >= @options.size(); self.call_selection(); end def pred_opt() if @current_option <= 0 then @current_option = @options.size - 1; else @current_option -= 1; end self.call_selection(); end def activate() @options[@current_option].action.call(); end def draw() @menu_view.draw(@view, @pos_x, @pos_y, @width, @height); @options.each_with_index do |opt, i| opt.text.draw( @view, @pos_x + @menu_view.border_width + @menu_view.border_width, @pos_y + @menu_view.border_height + @option_max_height * i, opt.width, opt.height); end @menu_view.sprites[:arrow_select].draw( @view, @pos_x + @menu_view.border_width, @pos_y + @menu_view.border_height + @option_max_height * @current_option, @menu_view.border_width, @menu_view.border_height); end protected def call_selection @options[@current_option].selection&.call(); end end end