summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/menu.rb220
1 files changed, 220 insertions, 0 deletions
diff --git a/lib/menu.rb b/lib/menu.rb
new file mode 100644
index 0000000..9f4c55c
--- /dev/null
+++ b/lib/menu.rb
@@ -0,0 +1,220 @@
+# Copyright 2022-2023 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 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, Vector4D.new(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, Vector4D.new(1.0/3.0, 1.0/3.0, 2.0/3.0, 2.0/3.0)),
+ box_top_left: Sprite.new(
+ @texture, Vector4D.new(0.0, 0.0, 1.0/3.0, 1.0/3.0)),
+ box_top: Sprite.new(
+ @texture, Vector4D.new(1.1/3.0, 0.0, 1.9/3.0, 1.0/3.0)),
+ box_top_right: Sprite.new(
+ @texture, Vector4D.new(2.0/3.0, 0.0, 1.0, 1.0/3.0)),
+ box_left: Sprite.new(
+ @texture, Vector4D.new(0.0, 1.1/3.0, 1.0/3.0, 1.9/3.0)),
+ box_right: Sprite.new(
+ @texture, Vector4D.new(2.0/3.0, 1.1/3.0, 1.0, 1.9/3.0)),
+ box_bottom_left: Sprite.new(
+ @texture, Vector4D.new(0.0, 2.0/3.0, 1.0/3.0, 1.0)),
+ box_bottom: Sprite.new(
+ @texture, Vector4D.new(1.1/3.0, 2.0/3.0, 1.9/3.0, 1.0)),
+ box_bottom_right: Sprite.new(
+ @texture, Vector4D.new(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, Vector4D.new(x, y, border_width, border_height));
+ @sprites[:box_top_right].draw(
+ view, Vector4D.new(
+ @border_width * (num_horizontal_sprites + 1) + x, y,
+ border_width, border_height));
+ @sprites[:box_bottom_left].draw(
+ view, Vector4D.new(
+ x, @border_height * (num_vertical_sprites + 1) + y,
+ border_width, border_height));
+ @sprites[:box_bottom_right].draw(
+ view, Vector4D.new(
+ @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, Vector4D.new(@border_width * (i + 1) + x, y,
+ border_width, border_height));
+ # Bottom
+ @sprites[:box_bottom].draw(
+ view, Vector4D.new(@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, Vector4D.new(
+ x, @border_height * (i + 1) + y, border_width, border_height));
+ # Right
+ @sprites[:box_right].draw(
+ view, Vector4D.new(
+ @border_width * (num_horizontal_sprites + 1) + x,
+ @border_height * (i + 1) + y,
+ border_width, border_height));
+ end
+ end
+ end
+
+ class Option
+ attr_reader(:action, :text, :width, :height);
+
+ def initialize(text, action, width, height);
+ @text = text;
+ @action = action;
+ @width = width;
+ @height = height;
+ 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, CandyGear::Vector4D.new(0, 0, 1.0, 1.0)),
+ opt[:action],
+ texture.width,
+ texture.height);
+ 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();
+ end
+
+ def pred_opt()
+ if @current_option <= 0 then
+ @current_option = @options.size - 1;
+ else
+ @current_option -= 1;
+ end
+ 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, Vector4D.new(
+ @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, Vector4D.new(
+ @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
+ end
+end