mirror of
https://github.com/cyberarm/cyberarm_engine.git
synced 2025-12-16 21:22:33 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9068a418c0 | |||
| 7ca6e1bc58 | |||
| a70f260bc6 | |||
| 8002708695 | |||
| 676545f3c7 | |||
| 4b417f9ab7 | |||
| a70c106387 | |||
| f662fabc56 | |||
| abb989f842 | |||
| 1e0d2004b5 | |||
| 92dd63dc1d | |||
| 20970e5aa9 | |||
| 76eb1a85d5 |
@@ -1,4 +1,4 @@
|
||||
# CyberarmEngine
|
||||

|
||||
|
||||
Yet Another Game Engine On Top Of Gosu
|
||||
|
||||
@@ -32,6 +32,8 @@ require "cyberarm_engine"
|
||||
|
||||
class Hello < CyberarmEngine::GuiState
|
||||
def setup
|
||||
background Gosu::Color::GRAY
|
||||
|
||||
stack do
|
||||
label "Hello World!"
|
||||
|
||||
@@ -43,15 +45,14 @@ class Hello < CyberarmEngine::GuiState
|
||||
end
|
||||
|
||||
class Window < CyberarmEngine::Window
|
||||
def initialize
|
||||
super
|
||||
def setup
|
||||
self.show_cursor = true
|
||||
|
||||
push_state(Hello)
|
||||
end
|
||||
end
|
||||
|
||||
Window.new.show
|
||||
Window.new(width: 800, height: 600, fullscreen: false, resizable: true).show
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
BIN
assets/textures/logo.png
Normal file
BIN
assets/textures/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -1,9 +1,8 @@
|
||||
CYBERARM_ENGINE_ROOT_PATH = File.expand_path("..", __dir__)
|
||||
|
||||
begin
|
||||
require File.expand_path("../../ffi-gosu/lib/gosu", File.dirname(__FILE__))
|
||||
rescue LoadError => e
|
||||
pp e
|
||||
if ARGV.join.include?("--ffi-gosu")
|
||||
require File.expand_path("../../ffi-gosu/lib/gosu", __dir__)
|
||||
else
|
||||
require "gosu"
|
||||
end
|
||||
require "json"
|
||||
@@ -62,3 +61,5 @@ require_relative "cyberarm_engine/model/model_object"
|
||||
require_relative "cyberarm_engine/model/parser"
|
||||
require_relative "cyberarm_engine/model/parsers/wavefront_parser"
|
||||
require_relative "cyberarm_engine/model/parsers/collada_parser" if defined?(Nokogiri)
|
||||
|
||||
require_relative "cyberarm_engine/builtin/intro_state"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module CyberarmEngine
|
||||
class Animator
|
||||
DEFAULT_TWEEN = :linear
|
||||
def initialize(start_time:, duration:, from:, to:, &block)
|
||||
def initialize(start_time:, duration:, from:, to:, tween: :linear, &block)
|
||||
@start_time = start_time
|
||||
@duration = duration
|
||||
@from = from.dup
|
||||
@to = to.dup
|
||||
@tween = tween
|
||||
@block = block
|
||||
end
|
||||
|
||||
@@ -14,18 +14,18 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def progress
|
||||
(@start_time.to_f + (Gosu.milliseconds - @start_time)) / (@start_time + @duration.to_f)
|
||||
((Gosu.milliseconds - @start_time) / @duration.to_f).clamp(0.0, 1.0)
|
||||
end
|
||||
|
||||
def complete?
|
||||
progress >= 1.0
|
||||
end
|
||||
|
||||
def transition(from, to, tween = DEFAULT_TWEEN)
|
||||
def transition(from = @from, to = @to, tween = @tween)
|
||||
from + (to - from) * send("tween_#{tween}", progress)
|
||||
end
|
||||
|
||||
def color_transition(from, to, _tween = DEFAULT_TWEEN)
|
||||
def color_transition(from = @from, to = @to, _tween = @tween)
|
||||
r = transition(from.red, to.red)
|
||||
g = transition(from.green, to.green)
|
||||
b = transition(from.blue, to.blue)
|
||||
@@ -34,7 +34,7 @@ module CyberarmEngine
|
||||
Gosu::Color.rgba(r, g, b, a)
|
||||
end
|
||||
|
||||
def color_hsv_transition(from, to, tween = DEFAULT_TWEEN)
|
||||
def color_hsv_transition(from = @from, to = @to, tween = @tween)
|
||||
hue = transition(from.hue, to.hue, tween)
|
||||
saturation = transition(from.saturation, to.saturation, tween)
|
||||
value = transition(from.value, to.value, tween)
|
||||
@@ -49,8 +49,8 @@ module CyberarmEngine
|
||||
t
|
||||
end
|
||||
|
||||
def tween_sine(t)
|
||||
Math.sin(t) * t
|
||||
def tween_ease_in_out(t)
|
||||
(-0.5 * (Math.cos(Math::PI * t) - 1))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
128
lib/cyberarm_engine/builtin/intro_state.rb
Normal file
128
lib/cyberarm_engine/builtin/intro_state.rb
Normal file
@@ -0,0 +1,128 @@
|
||||
module CyberarmEngine
|
||||
class IntroState < CyberarmEngine::GameState
|
||||
def setup
|
||||
@display_width = 800
|
||||
@display_height = 600
|
||||
|
||||
@title_size = 56
|
||||
@caption_size = 24
|
||||
|
||||
@title = CyberarmEngine::Text.new("", size: @title_size, shadow_color: 0xaa_222222)
|
||||
@caption = CyberarmEngine::Text.new("", size: @caption_size, shadow_color: 0xaa_222222)
|
||||
|
||||
@spacer_width = 256
|
||||
@spacer_height = 6
|
||||
@padding = 6
|
||||
|
||||
@cyberarm_engine_logo = get_image "#{CYBERARM_ENGINE_ROOT_PATH}/assets/textures/logo.png"
|
||||
|
||||
@gosu_logo = generate_proxy("Gosu", "Game Library", 0xff_111111)
|
||||
@ruby_logo = generate_proxy("Ruby", "Programming Language", 0xff_880000)
|
||||
@opengl_logo = generate_proxy("OpenGL", "Graphics API", 0xff_5586a4) if defined?(OpenGL)
|
||||
|
||||
base_time = Gosu.milliseconds
|
||||
|
||||
@animators = [
|
||||
Animator.new(start_time: base_time += 1000, duration: 100, from: 0.0, to: 1.0, tween: :ease_in_out),
|
||||
Animator.new(start_time: base_time += -500, duration: 1_000, from: 0.0, to: 1.0, tween: :ease_in_out),
|
||||
Animator.new(start_time: base_time += 500, duration: 1_000, from: 0.0, to: 1.0, tween: :ease_in_out),
|
||||
Animator.new(start_time: base_time += 500, duration: 1_000, from: 0.0, to: 1.0, tween: :ease_in_out),
|
||||
Animator.new(start_time: base_time + 500, duration: 1_000, from: 0.0, to: 1.0, tween: :ease_in_out),
|
||||
|
||||
Animator.new(start_time: Gosu.milliseconds + 250, duration: 500, from: 0.0, to: 1.0, tween: :ease_in_out) # CyberarmEngine LOGO
|
||||
]
|
||||
|
||||
@born_time = Gosu.milliseconds
|
||||
@continue_after = 5_000
|
||||
end
|
||||
|
||||
def draw
|
||||
Gosu.draw_rect(0, 0, window.width, window.height, 0xff_222222)
|
||||
|
||||
scale = (@display_width - @padding * 2).to_f / @cyberarm_engine_logo.width * @animators.last.transition
|
||||
|
||||
@cyberarm_engine_logo.draw_rot(
|
||||
window.width / 2,
|
||||
(window.height) / 2 - @cyberarm_engine_logo.height / 2 - @padding * 2,
|
||||
2,
|
||||
0,
|
||||
0.5,
|
||||
0.5,
|
||||
scale,
|
||||
scale
|
||||
)
|
||||
|
||||
Gosu.draw_rect(
|
||||
window.width / 2 - (@display_width / 2 + @padding),
|
||||
window.height / 2 - @spacer_height / 2,
|
||||
@display_width + @padding,
|
||||
@spacer_height * @animators[0].transition,
|
||||
Gosu::Color::WHITE
|
||||
)
|
||||
|
||||
@title.x = window.width / 2 - @title.width / 2
|
||||
@title.y = (window.height / 2 + (@spacer_height / 2) + @padding) * @animators[1].transition
|
||||
@title.text = "Powered By"
|
||||
|
||||
Gosu.clip_to(0, window.height / 2 + (@spacer_height / 2), window.width, @title.height) do
|
||||
@title.draw
|
||||
end
|
||||
|
||||
y = @title.y + @title.height * 2
|
||||
|
||||
Gosu.clip_to(0, y, window.width, @gosu_logo.height) do
|
||||
Gosu.translate(@opengl_logo.nil? ? @ruby_logo.width / 2 : 0, 0) do
|
||||
@gosu_logo.draw(
|
||||
window.width.to_f / 2 - @ruby_logo.width / 2 - (@ruby_logo.width - @padding),
|
||||
y * @animators[2].transition,
|
||||
2
|
||||
)
|
||||
@ruby_logo.draw(
|
||||
window.width.to_f / 2 - @ruby_logo.width / 2,
|
||||
y * @animators[3].transition,
|
||||
2
|
||||
)
|
||||
@opengl_logo&.draw(
|
||||
window.width.to_f / 2 - @ruby_logo.width / 2 + (@ruby_logo.width - @padding),
|
||||
y * @animators[4].transition,
|
||||
2
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@animators.each(&:update)
|
||||
|
||||
return unless Gosu.milliseconds - @born_time >= @continue_after
|
||||
|
||||
pop_state
|
||||
push_state(@options[:forward], @options[:forward_options] || {}) if @options[:forward]
|
||||
end
|
||||
|
||||
def button_down(_id)
|
||||
@continue_after = 0
|
||||
end
|
||||
|
||||
def generate_proxy(title, caption, color_hint)
|
||||
@title.text = title
|
||||
@caption.text = caption
|
||||
|
||||
width = @spacer_width + 2 * @padding
|
||||
height = @title_size + @caption_size + @spacer_height + 2 * @padding + @spacer_height
|
||||
|
||||
Gosu.record(width.ceil, height.ceil) do
|
||||
@title.x = (width - @padding * 2) / 2 - @title.width / 2
|
||||
@title.y = @padding
|
||||
@title.draw
|
||||
|
||||
Gosu.draw_rect(0, @padding + @title_size + @padding, @spacer_width, @spacer_height, Gosu::Color::WHITE)
|
||||
Gosu.draw_rect(1, @padding + @title_size + @padding + 1, @spacer_width - 2, @spacer_height - 2, color_hint)
|
||||
|
||||
@caption.x = (width - @padding * 2) / 2 - @caption.width / 2
|
||||
@caption.y = @padding + @title_size + @padding + @spacer_height + @padding
|
||||
@caption.draw
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -16,6 +16,10 @@ module CyberarmEngine
|
||||
window.pop_state
|
||||
end
|
||||
|
||||
def shift_state
|
||||
window.shift_state
|
||||
end
|
||||
|
||||
def show_cursor
|
||||
window.show_cursor
|
||||
end
|
||||
@@ -70,6 +74,7 @@ module CyberarmEngine
|
||||
else
|
||||
klass.new(path)
|
||||
end
|
||||
|
||||
hash[path] = instance
|
||||
asset = instance
|
||||
end
|
||||
|
||||
@@ -17,6 +17,11 @@ module CyberarmEngine
|
||||
def setup
|
||||
end
|
||||
|
||||
# Called immediately after setup returns.
|
||||
# GuiState uses this to set current_theme for ToolTip
|
||||
def post_setup
|
||||
end
|
||||
|
||||
def draw
|
||||
@game_objects.each(&:draw)
|
||||
end
|
||||
|
||||
@@ -54,7 +54,7 @@ module CyberarmEngine
|
||||
texture_id = tex_names_buf.unpack1("L2")
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, array_of_pixels)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, image.width, image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, array_of_pixels)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) if @retro
|
||||
|
||||
@@ -65,6 +65,15 @@ module CyberarmEngine
|
||||
font
|
||||
end
|
||||
|
||||
def swap_font(size, font_name = @font)
|
||||
if @size != size || @font != font_name
|
||||
@size = size
|
||||
@font = font_name
|
||||
|
||||
@textobject = check_cache(size, font_name)
|
||||
end
|
||||
end
|
||||
|
||||
def text=(string)
|
||||
@rendered_shadow = nil
|
||||
@text = string
|
||||
@@ -140,6 +149,7 @@ module CyberarmEngine
|
||||
@textobject.send(method, @text, _x + @shadow_size, _y, @z, @factor_x, @factor_y, white, :add)
|
||||
@textobject.send(method, @text, _x + @shadow_size, _y + @shadow_size, @z, @factor_x, @factor_y, white, :add)
|
||||
end
|
||||
|
||||
@rendered_shadow.draw(@x - @shadow_size, @y - @shadow_size, @z, @factor_x, @factor_y, shadow_color)
|
||||
end
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ module CyberarmEngine
|
||||
"Tagline",
|
||||
"Caption",
|
||||
"Para",
|
||||
"Inscription"
|
||||
"Inscription",
|
||||
"Link"
|
||||
].each do |const|
|
||||
define_method(:"#{const.downcase}") do |text, options = {}, &block|
|
||||
options[:parent] = element_parent
|
||||
@@ -97,7 +98,7 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def background(color = Gosu::Color::NONE)
|
||||
element_parent.style.background = color
|
||||
element_parent.style.default[:background] = color
|
||||
end
|
||||
|
||||
def theme(theme)
|
||||
|
||||
@@ -18,6 +18,8 @@ module CyberarmEngine
|
||||
@visible = @options[:visible].nil? ? true : @options[:visible]
|
||||
@tip = @options[:tip] || ""
|
||||
|
||||
@debug_color = @options[:debug_color].nil? ? Gosu::Color::RED : @options[:debug_color]
|
||||
|
||||
@style = Style.new(options)
|
||||
|
||||
@root ||= nil
|
||||
@@ -36,21 +38,29 @@ module CyberarmEngine
|
||||
@style.background_canvas = Background.new
|
||||
@style.border_canvas = BorderCanvas.new(element: self)
|
||||
|
||||
@style_event = :default
|
||||
|
||||
stylize
|
||||
|
||||
default_events
|
||||
|
||||
root.gui_state.request_focus(self) if @options[:autofocus]
|
||||
end
|
||||
|
||||
def stylize
|
||||
set_static_position
|
||||
set_border_thickness(@style.border_thickness)
|
||||
|
||||
set_padding(@style.padding)
|
||||
set_padding
|
||||
set_margin
|
||||
|
||||
set_margin(@style.margin)
|
||||
set_background
|
||||
|
||||
set_background(@style.background)
|
||||
set_border_color(@style.border_color)
|
||||
set_border_thickness
|
||||
set_border_color
|
||||
end
|
||||
|
||||
def safe_style_fetch(*args)
|
||||
@style.hash.dig(@style_event, *args) || @style.hash.dig(:default, *args) || default(*args)
|
||||
end
|
||||
|
||||
def set_static_position
|
||||
@@ -58,47 +68,65 @@ module CyberarmEngine
|
||||
@y = @style.y if @style.y != 0
|
||||
end
|
||||
|
||||
def set_background(background)
|
||||
@style.background = background
|
||||
@style.background_canvas.background = background
|
||||
def set_background
|
||||
@style.background = safe_style_fetch(:background)
|
||||
|
||||
@style.background_canvas.background = @style.background
|
||||
end
|
||||
|
||||
def set_border_thickness(border_thickness)
|
||||
@style.border_thickness = border_thickness
|
||||
def set_border_thickness
|
||||
@style.border_thickness = safe_style_fetch(:border_thickness)
|
||||
|
||||
@style.border_thickness_left = default(:border_thickness_left) || @style.border_thickness
|
||||
@style.border_thickness_right = default(:border_thickness_right) || @style.border_thickness
|
||||
@style.border_thickness_top = default(:border_thickness_top) || @style.border_thickness
|
||||
@style.border_thickness_bottom = default(:border_thickness_bottom) || @style.border_thickness
|
||||
@style.border_thickness_left = safe_style_fetch(:border_thickness_left) || @style.border_thickness
|
||||
@style.border_thickness_right = safe_style_fetch(:border_thickness_right) || @style.border_thickness
|
||||
@style.border_thickness_top = safe_style_fetch(:border_thickness_top) || @style.border_thickness
|
||||
@style.border_thickness_bottom = safe_style_fetch(:border_thickness_bottom) || @style.border_thickness
|
||||
end
|
||||
|
||||
def set_border_color(color)
|
||||
@style.border_color = color
|
||||
def set_border_color
|
||||
@style.border_color = safe_style_fetch(:border_color)
|
||||
|
||||
@style.border_color_left = default(:border_color_left) || @style.border_color
|
||||
@style.border_color_right = default(:border_color_right) || @style.border_color
|
||||
@style.border_color_top = default(:border_color_top) || @style.border_color
|
||||
@style.border_color_bottom = default(:border_color_bottom) || @style.border_color
|
||||
@style.border_color_left = safe_style_fetch(:border_color_left) || @style.border_color
|
||||
@style.border_color_right = safe_style_fetch(:border_color_right) || @style.border_color
|
||||
@style.border_color_top = safe_style_fetch(:border_color_top) || @style.border_color
|
||||
@style.border_color_bottom = safe_style_fetch(:border_color_bottom) || @style.border_color
|
||||
|
||||
@style.border_canvas.color = color
|
||||
@style.border_canvas.color = [
|
||||
@style.border_color_top,
|
||||
@style.border_color_right,
|
||||
@style.border_color_bottom,
|
||||
@style.border_color_left
|
||||
]
|
||||
end
|
||||
|
||||
def set_padding(padding)
|
||||
@style.padding = padding
|
||||
def set_padding
|
||||
@style.padding = safe_style_fetch(:padding)
|
||||
|
||||
@style.padding_left = default(:padding_left) || @style.padding
|
||||
@style.padding_right = default(:padding_right) || @style.padding
|
||||
@style.padding_top = default(:padding_top) || @style.padding
|
||||
@style.padding_bottom = default(:padding_bottom) || @style.padding
|
||||
@style.padding_left = safe_style_fetch(:padding_left) || @style.padding
|
||||
@style.padding_right = safe_style_fetch(:padding_right) || @style.padding
|
||||
@style.padding_top = safe_style_fetch(:padding_top) || @style.padding
|
||||
@style.padding_bottom = safe_style_fetch(:padding_bottom) || @style.padding
|
||||
end
|
||||
|
||||
def set_margin(margin)
|
||||
@style.margin = margin
|
||||
def set_margin
|
||||
@style.margin = safe_style_fetch(:margin)
|
||||
|
||||
@style.margin_left = default(:margin_left) || @style.margin
|
||||
@style.margin_right = default(:margin_right) || @style.margin
|
||||
@style.margin_top = default(:margin_top) || @style.margin
|
||||
@style.margin_bottom = default(:margin_bottom) || @style.margin
|
||||
@style.margin_left = safe_style_fetch(:margin_left) || @style.margin
|
||||
@style.margin_right = safe_style_fetch(:margin_right) || @style.margin
|
||||
@style.margin_top = safe_style_fetch(:margin_top) || @style.margin
|
||||
@style.margin_bottom = safe_style_fetch(:margin_bottom) || @style.margin
|
||||
end
|
||||
|
||||
def update_styles(event = :default)
|
||||
_style = @style.send(event)
|
||||
@style_event = event
|
||||
|
||||
if @text.is_a?(CyberarmEngine::Text)
|
||||
@text.color = _style&.dig(:color) || @style.default[:color]
|
||||
@text.swap_font(_style&.dig(:text_size) || @style.default[:text_size], _style&.dig(:font) || @style.default[:font])
|
||||
end
|
||||
|
||||
(root&.gui_state || @gui_state).request_recalculate
|
||||
end
|
||||
|
||||
def default_events
|
||||
@@ -116,11 +144,74 @@ module CyberarmEngine
|
||||
event(:hover)
|
||||
event(:leave)
|
||||
|
||||
event(:focus)
|
||||
event(:blur)
|
||||
|
||||
event(:changed)
|
||||
end
|
||||
|
||||
def enter(_sender)
|
||||
@focus = false unless window.button_down?(Gosu::MsLeft)
|
||||
|
||||
if !@enabled
|
||||
update_styles(:disabled)
|
||||
elsif @focus
|
||||
update_styles(:active)
|
||||
else
|
||||
update_styles(:hover)
|
||||
end
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def left_mouse_button(_sender, _x, _y)
|
||||
@focus = true
|
||||
|
||||
unless @enabled
|
||||
update_styles(:disabled)
|
||||
else
|
||||
update_styles(:active)
|
||||
end
|
||||
|
||||
window.current_state.focus = self
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def released_left_mouse_button(sender, _x, _y)
|
||||
enter(sender)
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def clicked_left_mouse_button(_sender, _x, _y)
|
||||
@block&.call(self) if @enabled && !self.is_a?(Container)
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def leave(_sender)
|
||||
if @enabled
|
||||
update_styles
|
||||
else
|
||||
update_styles(:disabled)
|
||||
end
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def blur(_sender)
|
||||
@focus = false
|
||||
|
||||
if @enabled
|
||||
update_styles
|
||||
else
|
||||
update_styles(:disabled)
|
||||
end
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def enabled?
|
||||
@enabled
|
||||
end
|
||||
@@ -157,6 +248,31 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
def debug_draw
|
||||
return if defined?(GUI_DEBUG_ONLY_ELEMENT) && self.class == GUI_DEBUG_ONLY_ELEMENT
|
||||
|
||||
Gosu.draw_line(
|
||||
x, y, @debug_color,
|
||||
x + outer_width, y, @debug_color,
|
||||
Float::INFINITY
|
||||
)
|
||||
Gosu.draw_line(
|
||||
x + outer_width, y, @debug_color,
|
||||
x + outer_width, y + outer_height, @debug_color,
|
||||
Float::INFINITY
|
||||
)
|
||||
Gosu.draw_line(
|
||||
x + outer_width, y + outer_height, @debug_color,
|
||||
x, y + outer_height, @debug_color,
|
||||
Float::INFINITY
|
||||
)
|
||||
Gosu.draw_line(
|
||||
x, outer_height, @debug_color,
|
||||
x, y, @debug_color,
|
||||
Float::INFINITY
|
||||
)
|
||||
end
|
||||
|
||||
def update
|
||||
end
|
||||
|
||||
@@ -255,7 +371,7 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def background=(_background)
|
||||
@style.background_canvas.background = (_background)
|
||||
@style.background_canvas.background = _background
|
||||
update_background
|
||||
end
|
||||
|
||||
@@ -278,11 +394,9 @@ module CyberarmEngine
|
||||
@root = parent
|
||||
|
||||
loop do
|
||||
if @root.parent.nil?
|
||||
break
|
||||
else
|
||||
@root = @root.parent
|
||||
end
|
||||
break unless @root&.parent
|
||||
|
||||
@root = @root.parent
|
||||
end
|
||||
end
|
||||
|
||||
@@ -293,6 +407,12 @@ module CyberarmEngine
|
||||
@gui_state != nil
|
||||
end
|
||||
|
||||
def focus(_)
|
||||
warn "#{self.class}#focus was not overridden!"
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def recalculate
|
||||
raise "#{self.class}#recalculate was not overridden!"
|
||||
end
|
||||
@@ -311,5 +431,9 @@ module CyberarmEngine
|
||||
def to_s
|
||||
"#{self.class} x=#{x} y=#{y} width=#{width} height=#{height} value=#{value.is_a?(String) ? "\"#{value}\"" : value}"
|
||||
end
|
||||
|
||||
def inspect
|
||||
to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,69 +34,6 @@ module CyberarmEngine
|
||||
@text.draw
|
||||
end
|
||||
|
||||
def enter(_sender)
|
||||
@focus = false unless window.button_down?(Gosu::MsLeft)
|
||||
|
||||
if !@enabled
|
||||
@style.background_canvas.background = @style.disabled[:background]
|
||||
@text.color = @style.disabled[:color]
|
||||
elsif @focus
|
||||
@style.background_canvas.background = @style.active[:background]
|
||||
@text.color = @style.active[:color]
|
||||
else
|
||||
@style.background_canvas.background = @style.hover[:background]
|
||||
@text.color = @style.hover[:color]
|
||||
end
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def left_mouse_button(_sender, _x, _y)
|
||||
@focus = true
|
||||
|
||||
unless @enabled
|
||||
@style.background_canvas.background = @style.disabled[:background]
|
||||
@text.color = @style.disabled[:color]
|
||||
else
|
||||
@style.background_canvas.background = @style.active[:background]
|
||||
@text.color = @style.active[:color]
|
||||
end
|
||||
|
||||
window.current_state.focus = self
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def released_left_mouse_button(sender, _x, _y)
|
||||
enter(sender)
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def clicked_left_mouse_button(_sender, _x, _y)
|
||||
@block.call(self) if @enabled && @block
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def leave(_sender)
|
||||
unless @enabled
|
||||
@style.background_canvas.background = @style.disabled[:background]
|
||||
@text.color = @style.disabled[:color]
|
||||
else
|
||||
@style.background_canvas.background = @style.background
|
||||
@text.color = @style.color
|
||||
end
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def blur(_sender)
|
||||
@focus = false
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def recalculate
|
||||
unless @enabled
|
||||
@style.background_canvas.background = @style.disabled[:background]
|
||||
|
||||
@@ -5,8 +5,11 @@ module CyberarmEngine
|
||||
super(options, block)
|
||||
options[:toggled] = options[:checked]
|
||||
|
||||
options[:parent] = self
|
||||
@toggle_button = ToggleButton.new(options)
|
||||
@label = TextBlock.new(text, options)
|
||||
|
||||
options[:parent] = self
|
||||
@label = TextBlock.new(text, options)
|
||||
|
||||
@label.subscribe(:holding_left_mouse_button) do |sender, x, y|
|
||||
@toggle_button.left_mouse_button(sender, x, y)
|
||||
|
||||
@@ -60,30 +60,13 @@ module CyberarmEngine
|
||||
Gosu.clip_to(@x, @y, width, height) do
|
||||
@children.each(&:draw)
|
||||
end
|
||||
end
|
||||
|
||||
if false # DEBUG
|
||||
Gosu.flush
|
||||
def debug_draw
|
||||
super
|
||||
|
||||
Gosu.draw_line(
|
||||
x, y, Gosu::Color::RED,
|
||||
x + outer_width, y, Gosu::Color::RED,
|
||||
Float::INFINITY
|
||||
)
|
||||
Gosu.draw_line(
|
||||
x + outer_width, y, Gosu::Color::RED,
|
||||
x + outer_width, y + outer_height, Gosu::Color::RED,
|
||||
Float::INFINITY
|
||||
)
|
||||
Gosu.draw_line(
|
||||
x + outer_width, y + outer_height, Gosu::Color::RED,
|
||||
x, y + outer_height, Gosu::Color::RED,
|
||||
Float::INFINITY
|
||||
)
|
||||
Gosu.draw_line(
|
||||
x, outer_height, Gosu::Color::RED,
|
||||
x, y, Gosu::Color::RED,
|
||||
Float::INFINITY
|
||||
)
|
||||
@children.each do |child|
|
||||
child.debug_draw
|
||||
end
|
||||
end
|
||||
|
||||
@@ -205,7 +188,6 @@ module CyberarmEngine
|
||||
|
||||
def mouse_wheel_up(sender, x, y)
|
||||
return unless @style.scroll
|
||||
return if height < max_scroll_height
|
||||
|
||||
if @scroll_position.y < 0
|
||||
@scroll_position.y += @scroll_speed
|
||||
@@ -218,7 +200,8 @@ module CyberarmEngine
|
||||
|
||||
def mouse_wheel_down(sender, x, y)
|
||||
return unless @style.scroll
|
||||
return if height < max_scroll_height
|
||||
|
||||
return unless height < scroll_height
|
||||
|
||||
if @scroll_position.y.abs < max_scroll_height
|
||||
@scroll_position.y -= @scroll_speed
|
||||
@@ -229,6 +212,21 @@ module CyberarmEngine
|
||||
end
|
||||
end
|
||||
|
||||
def scroll_top
|
||||
@scroll_position.y
|
||||
end
|
||||
|
||||
def scroll_top=(n)
|
||||
n = 0 if n <= 0
|
||||
@scroll_position.y = -n
|
||||
|
||||
if max_scroll_height.positive?
|
||||
@scroll_position.y = -max_scroll_height if @scroll_position.y.abs > max_scroll_height
|
||||
else
|
||||
@scroll_position.y = 0
|
||||
end
|
||||
end
|
||||
|
||||
def value
|
||||
@children.map { |c| c.class }.join(", ")
|
||||
end
|
||||
|
||||
@@ -59,6 +59,8 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def update
|
||||
@style_event = :active if @focus
|
||||
|
||||
@text.text = if @type == :password
|
||||
default(:password_character) * @text_input.text.length
|
||||
else
|
||||
@@ -194,28 +196,27 @@ module CyberarmEngine
|
||||
:handled
|
||||
end
|
||||
|
||||
def enter(_sender)
|
||||
if @focus
|
||||
@style.background_canvas.background = default(:active, :background)
|
||||
@text.color = default(:active, :color)
|
||||
else
|
||||
@style.background_canvas.background = default(:hover, :background)
|
||||
@text.color = default(:hover, :color)
|
||||
end
|
||||
def focus(sender)
|
||||
super
|
||||
|
||||
window.text_input = @text_input
|
||||
@text_input.caret_pos = @text_input.selection_start = @text_input.text.length
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def leave(sender)
|
||||
super unless @focus
|
||||
def enter(sender)
|
||||
_has_focus = @focus
|
||||
|
||||
super
|
||||
|
||||
@focus = _has_focus
|
||||
|
||||
:handled
|
||||
end
|
||||
|
||||
def blur(_sender)
|
||||
@focus = false
|
||||
@style.background_canvas.background = default(:background)
|
||||
@text.color = default(:color)
|
||||
super
|
||||
window.text_input = nil
|
||||
|
||||
:handled
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
module CyberarmEngine
|
||||
class Element
|
||||
class TextBlock < Element
|
||||
def initialize(text, options = {}, block = nil)
|
||||
super(options, block)
|
||||
|
||||
@text = Text.new(
|
||||
text, font: @options[:font], z: @z, color: @options[:color],
|
||||
size: @options[:text_size], shadow: @options[:text_shadow],
|
||||
shadow_size: @options[:text_shadow_size],
|
||||
shadow_color: @options[:text_shadow_color]
|
||||
)
|
||||
|
||||
@raw_text = text
|
||||
end
|
||||
|
||||
def render
|
||||
@text.draw
|
||||
end
|
||||
|
||||
def clicked_left_mouse_button(_sender, _x, _y)
|
||||
@block&.call(self) if @enabled
|
||||
|
||||
# return :handled
|
||||
end
|
||||
|
||||
def recalculate
|
||||
@width = 0
|
||||
@height = 0
|
||||
|
||||
_width = dimensional_size(@style.width, :width)
|
||||
_height = dimensional_size(@style.height, :height)
|
||||
|
||||
handle_text_wrapping(_width)
|
||||
|
||||
@width = _width || @text.width.round
|
||||
@height = _height || @text.height.round
|
||||
|
||||
@text.y = @style.border_thickness_top + @style.padding_top + @y
|
||||
@text.z = @z + 3
|
||||
|
||||
if (text_alignment = @options[:text_align])
|
||||
case text_alignment
|
||||
when :left
|
||||
@text.x = @style.border_thickness_left + @style.padding_left + @x
|
||||
when :center
|
||||
@text.x = if @text.width <= outer_width
|
||||
@x + outer_width / 2 - @text.width / 2
|
||||
else # Act as left aligned
|
||||
@style.border_thickness_left + @style.padding_left + @x
|
||||
end
|
||||
when :right
|
||||
@text.x = @x + outer_width - (@text.width + @style.border_thickness_right + @style.padding_right)
|
||||
end
|
||||
end
|
||||
|
||||
update_background
|
||||
end
|
||||
|
||||
def handle_text_wrapping(max_width)
|
||||
max_width ||= @parent&.width
|
||||
max_width ||= @x - (window.width + noncontent_width)
|
||||
wrap_behavior = style.text_wrap
|
||||
copy = @raw_text.to_s.dup
|
||||
|
||||
if max_width >= line_width(copy[0]) && line_width(copy) > max_width && wrap_behavior != :none
|
||||
breaks = []
|
||||
line_start = 0
|
||||
line_end = copy.length
|
||||
|
||||
while line_start != copy.length
|
||||
if line_width(copy[line_start...line_end]) > max_width
|
||||
line_end = ((line_end - line_start) / 2.0)
|
||||
elsif line_end < copy.length && line_width(copy[line_start...line_end + 1]) < max_width
|
||||
# To small, grow!
|
||||
# TODO: find a more efficient way
|
||||
line_end += 1
|
||||
|
||||
else # FOUND IT!
|
||||
entering_line_end = line_end.floor
|
||||
max_reach = line_end.floor - line_start < 63 ? line_end.floor - line_start : 63
|
||||
reach = 0
|
||||
|
||||
if wrap_behavior == :word_wrap
|
||||
max_reach.times do |i|
|
||||
reach = i
|
||||
break if copy[line_end.floor - i].to_s.match(/[[:punct:]]| /)
|
||||
end
|
||||
|
||||
puts "Max width: #{max_width}/#{line_width(@raw_text)} Reach: {#{reach}/#{max_reach}} Line Start: #{line_start}/#{line_end.floor} (#{copy.length}|#{@raw_text.length}) [#{entering_line_end}] '#{copy}' {#{copy[line_start...line_end]}}"
|
||||
line_end = line_end.floor - reach + 1 if reach != max_reach # Add +1 to walk in front of punctuation
|
||||
end
|
||||
|
||||
breaks << line_end.floor
|
||||
line_start = line_end.floor
|
||||
line_end = copy.length
|
||||
|
||||
break if entering_line_end == copy.length || reach == max_reach
|
||||
end
|
||||
end
|
||||
|
||||
breaks.each_with_index do |pos, index|
|
||||
copy.insert(pos + index, "\n") if pos + index >= 0 && pos + index < copy.length
|
||||
end
|
||||
end
|
||||
|
||||
@text.text = copy
|
||||
end
|
||||
|
||||
def line_width(text)
|
||||
(@x + @text.textobject.markup_width(text) + noncontent_width)
|
||||
end
|
||||
|
||||
def value
|
||||
@raw_text
|
||||
end
|
||||
|
||||
def value=(value)
|
||||
@raw_text = value.to_s.chomp
|
||||
|
||||
old_width = width
|
||||
old_height = height
|
||||
recalculate
|
||||
|
||||
root.gui_state.request_recalculate if old_width != width || old_height != height
|
||||
|
||||
publish(:changed, self.value)
|
||||
end
|
||||
end
|
||||
|
||||
class Banner < TextBlock
|
||||
end
|
||||
|
||||
class Title < TextBlock
|
||||
end
|
||||
|
||||
class Subtitle < TextBlock
|
||||
end
|
||||
|
||||
class Tagline < TextBlock
|
||||
end
|
||||
|
||||
class Caption < TextBlock
|
||||
end
|
||||
|
||||
class Para < TextBlock
|
||||
end
|
||||
|
||||
class Inscription < TextBlock
|
||||
end
|
||||
|
||||
# TODO: Remove in version 0.16.0+
|
||||
class Label < TextBlock
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -18,12 +18,6 @@ module CyberarmEngine
|
||||
@text.draw
|
||||
end
|
||||
|
||||
def clicked_left_mouse_button(_sender, _x, _y)
|
||||
@block&.call(self) if @enabled
|
||||
|
||||
# return :handled
|
||||
end
|
||||
|
||||
def recalculate
|
||||
@width = 0
|
||||
@height = 0
|
||||
@@ -152,5 +146,8 @@ module CyberarmEngine
|
||||
|
||||
class ToolTip < TextBlock
|
||||
end
|
||||
|
||||
class Link < TextBlock
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,12 +26,15 @@ module CyberarmEngine
|
||||
@dragging_element = nil
|
||||
@pending_recalculate_request = false
|
||||
|
||||
@tip = Element::ToolTip.new("", parent: @root_container, z: Float::INFINITY)
|
||||
@menu = nil
|
||||
@min_drag_distance = 0
|
||||
@mouse_pos = Vector.new
|
||||
end
|
||||
|
||||
def post_setup
|
||||
@tip = Element::ToolTip.new("", parent: @root_container, z: Float::INFINITY, theme: current_theme)
|
||||
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)
|
||||
@@ -55,6 +58,12 @@ module CyberarmEngine
|
||||
Gosu.flush
|
||||
@tip.draw
|
||||
end
|
||||
|
||||
if defined?(GUI_DEBUG)
|
||||
Gosu.flush
|
||||
|
||||
@root_container.debug_draw
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -66,6 +75,13 @@ module CyberarmEngine
|
||||
@pending_recalculate_request = false
|
||||
end
|
||||
|
||||
if @pending_focus_request
|
||||
@pending_focus_request = false
|
||||
|
||||
self.focus = @pending_focus_element
|
||||
@pending_focus_element.publish(:focus)
|
||||
end
|
||||
|
||||
@menu&.update
|
||||
super
|
||||
|
||||
@@ -90,7 +106,7 @@ module CyberarmEngine
|
||||
@tip.x = window.mouse_x - @tip.width / 2
|
||||
@tip.x = 0 if @tip.x < 0
|
||||
@tip.x = window.width - @tip.width if @tip.x + @tip.width > window.width
|
||||
@tip.y = window.mouse_y - @tip.height
|
||||
@tip.y = window.mouse_y - (@tip.height + 5)
|
||||
@tip.y = 0 if @tip.y < 0
|
||||
@tip.y = window.height - @tip.height if @tip.y + @tip.height > window.height
|
||||
@tip.update
|
||||
@@ -215,6 +231,11 @@ module CyberarmEngine
|
||||
@pending_recalculate_request = true
|
||||
end
|
||||
|
||||
def request_focus(element)
|
||||
@pending_focus_request = true
|
||||
@pending_focus_element = element
|
||||
end
|
||||
|
||||
def show_menu(list_box)
|
||||
@menu = list_box
|
||||
end
|
||||
@@ -222,5 +243,14 @@ module CyberarmEngine
|
||||
def hide_menu
|
||||
@menu = nil
|
||||
end
|
||||
|
||||
def to_s
|
||||
# "#{self.class} children=#{@children.map { |c| c.to_s }}"
|
||||
@root_container.to_s
|
||||
end
|
||||
|
||||
def inspect
|
||||
to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,8 +17,20 @@ end
|
||||
|
||||
module CyberarmEngine
|
||||
class Style
|
||||
attr_reader :hash
|
||||
|
||||
def initialize(hash = {})
|
||||
@hash = Marshal.load(Marshal.dump(hash))
|
||||
h = Marshal.load(Marshal.dump(hash))
|
||||
|
||||
h[:default] = {}
|
||||
|
||||
h.each do |key, value|
|
||||
next if value.is_a?(Hash)
|
||||
|
||||
h[:default][key] = value
|
||||
end
|
||||
|
||||
@hash = h
|
||||
end
|
||||
|
||||
def method_missing(method, *args)
|
||||
@@ -27,9 +39,8 @@ module CyberarmEngine
|
||||
|
||||
@hash[method.to_s.sub("=", "").to_sym] = args.first
|
||||
|
||||
elsif args.size == 0
|
||||
elsif args.empty?
|
||||
@hash[method]
|
||||
|
||||
else
|
||||
raise ArgumentError, "Did not expect arguments"
|
||||
end
|
||||
|
||||
@@ -64,6 +64,10 @@ module CyberarmEngine
|
||||
border_radius: 0
|
||||
},
|
||||
|
||||
Container: { # < Element (Base class for Stack and Flow)
|
||||
debug_color: Gosu::Color::YELLOW
|
||||
},
|
||||
|
||||
Button: { # < Label
|
||||
margin: 1,
|
||||
padding: 4,
|
||||
@@ -155,11 +159,28 @@ module CyberarmEngine
|
||||
border_color: 0xffaaaaaa,
|
||||
background: 0xff404040
|
||||
},
|
||||
Link: { # < TextBlock
|
||||
color: Gosu::Color::BLUE,
|
||||
border_thickness: 1,
|
||||
border_bottom_color: Gosu::Color::BLUE,
|
||||
hover: {
|
||||
color: 0xff_ff00ff,
|
||||
border_bottom_color: 0xff_ff00ff
|
||||
},
|
||||
active: {
|
||||
color: 0xff_ff0000,
|
||||
border_bottom_color: 0xff_ff0000
|
||||
}
|
||||
},
|
||||
|
||||
ToggleButton: { # < Button
|
||||
checkmark: "√"
|
||||
},
|
||||
|
||||
CheckBox: { # < Flow
|
||||
text_wrap: :none
|
||||
},
|
||||
|
||||
Progress: { # < Element
|
||||
width: 250,
|
||||
height: 36,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module CyberarmEngine
|
||||
NAME = "InDev".freeze
|
||||
VERSION = "0.17.0".freeze
|
||||
VERSION = "0.18.0".freeze
|
||||
end
|
||||
|
||||
@@ -77,10 +77,12 @@ module CyberarmEngine
|
||||
if klass.instance_of?(klass.class) && defined?(klass.options)
|
||||
@states << klass
|
||||
klass.setup if options[:setup]
|
||||
klass.post_setup if options[:setup]
|
||||
else
|
||||
@states << klass.new(options) if child_of?(klass, GameState)
|
||||
@states << klass.new if child_of?(klass, Element::Container)
|
||||
current_state.setup if current_state.instance_of?(klass) && options[:setup]
|
||||
current_state.post_setup if current_state.instance_of?(klass) && options[:setup]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -93,7 +95,7 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
def previous_state
|
||||
if @states.size > 1 && state = @states[@states.size - 2]
|
||||
if @states.size > 1 && (state = @states[@states.size - 2])
|
||||
state
|
||||
end
|
||||
end
|
||||
@@ -102,6 +104,10 @@ module CyberarmEngine
|
||||
@states.pop
|
||||
end
|
||||
|
||||
def shift_state
|
||||
@states.shift
|
||||
end
|
||||
|
||||
# Sourced from https://gist.github.com/ippa/662583
|
||||
def draw_circle(cx, cy, r, z = 9999, color = Gosu::Color::GREEN, step = 10)
|
||||
0.step(360, step) do |a1|
|
||||
|
||||
Reference in New Issue
Block a user