Skip to content

Instantly share code, notes, and snippets.

@aoitaku
Created December 21, 2016 09:32
Show Gist options
  • Save aoitaku/333278322eda38fa79482dbe6387a846 to your computer and use it in GitHub Desktop.
Save aoitaku/333278322eda38fa79482dbe6387a846 to your computer and use it in GitHub Desktop.
quincite_for_rgss
module Quincite
module UI
module Event
def fire(type, current_target, target, *args)
__send__(type, current_target, target, *args)
end
end
module EventHandler
attr_reader :event_handlers
module EventHandlerUtils
def event_handler(*names) names.each {|name| class_eval(<<-EOD) } end
def on_#{name}(*args)
event_handlers[:#{name}] and instance_exec(*args, &event_handlers[:#{name}])
end
EOD
end
def self.included(klass)
klass.extend(EventHandlerUtils)
end
end
module EventDispatcher
attr_accessor :event_listener
attr_reader :event
def initialize(event_listener)
@event_listener = event_listener
end
end
end
end
module Quincite
module UI
class Color
attr_accessor :red, :green, :blue, :alpha
def initialize(rgb, alpha=255)
@red, @green, @blue = *rgb
@alpha = alpha
end
def self.from_array(color)
case color.size
when 3
self.new(color)
when 4
self.new(color.take(3), color.last)
else
nil
end
end
def self.from_hex(color)
self.new([
color >> 16 & 0xff,
color >> 8 & 0xff,
color & 0xff
])
end
def self.from_hexstring(color)
sefl.new([
color[1..2].hex,
color[3..4].hex,
color[5..6].hex
])
end
def self.[](*rgba)
self.from_array(rgba)
end
end
def self.Color(color)
case color
when Array
Color.from_array(color)
when Fixnum
Color.from_hex(color)
when /^#[0-9a-fA-F]{6}$/
Color.from_hexstring(color)
else
nil
end
end
end
end
module Quincite
module UI
class Style
extend Forwardable
attr_accessor :position
attr_accessor :top, :left, :bottom, :right
attr_accessor :width, :height
attr_reader :margin, :padding
attr_accessor :layout
attr_accessor :justify_content
attr_accessor :align_items
attr_accessor :break_after
attr_accessor :visible
def_delegator :@bg, :color, :bg_color
def_delegator :@bg, :color=, :bg_color=
def_delegator :@bg, :image, :bg_image
def_delegator :@bg, :image=, :bg_image=
def_delegator :@bg, :bg=
def_delegator :@border, :width, :border_width
def_delegator :@border, :width=, :border_width=
def_delegator :@border, :color, :border_color
def_delegator :@border, :color=, :border_color=
def_delegator :@border, :border=
def initialize
@position = :relative
@top = nil
@left = nil
@bottom = nil
@right = nil
@width = nil
@height = nil
@margin = [0, 0, 0, 0]
@padding = [0, 0, 0, 0]
@layout = nil
@justify_content = nil
@align_items = nil
@break_after = false
@bg = Background.new
@border = Border.new
@visible = true
end
def margin=(args)
case args
when Numeric
@margin = [args] * 4
when Array
case args.size
when 1
@margin = args * 4
when 2
@margin = args * 2
when 3
@margin = [*args, args[1]]
when 4
@margin = args
else
@margin = args[0...4]
end
else
@margin = [0, 0, 0, 0]
end
end
def margin_top
@margin[0]
end
def margin_right
@margin[1]
end
def margin_bottom
@margin[2]
end
def margin_left
@margin[3]
end
def padding=(args)
case args
when Numeric
@padding = [args] * 4
when Array
case args.size
when 1
@padding = args * 4
when 2
@padding = args * 2
when 3
@padding = [*args, args[1]]
when 4
@padding = args
else
@padding = args[0...4]
end
else
@padding = [0, 0, 0, 0]
end
end
def padding_top
@padding[0]
end
def padding_right
@padding[1]
end
def padding_bottom
@padding[2]
end
def padding_left
@padding[3]
end
class Background
attr_accessor :image, :color
def initialize
@image = nil
@color = nil
end
def bg=(bg)
case bg
when Hash
@image = bg[:image]
@color = UI.Color(bg[:color]) || Color[255, 255, 255, 255]
else
@image = nil
@color = nil
end
end
end
class Border
attr_accessor :width, :color
def initialize
@width = 0
@color = Color[0, 0, 0, 0]
end
def border=(border)
case border
when Hash
@width = border[:width] || 1
@color = UI.Color(border[:color]) || Color[255, 255, 255, 255]
else
@width = nil
@color = nil
end
end
end
end
end
end
module Quincite
module AnonymousProxy
def anon_proxy(name, sup=BasicObject, &proc)
instance_variable_set(:"@#{name}", Class.new(sup, &proc).new)
class_eval("def self.#{name}() @#{name} end")
end
end
end
module Quincite
module UI
extend AnonymousProxy
anon_proxy :gateway do
def method_missing(name, *args, &proc)
if proc
UI.resolve_method(name, *args, &proc)
else
UI.resolve_method(name, *args)
end
end
def respond_to?(name)
UI.worker.acceptable?(name)
end
end
anon_proxy :worker, Array do
def transfer(name, *args)
if block_given?
last.__send__(name, *args, &proc)
else
last.__send__(name, *args)
end
end
def acceptable?(name)
last.respond_to?(name)
end
def run_with_stack(o)
push o
yield
pop
end
end
@namespace = self
def self.namespace=(namespace)
@namespace = namespace
end
def self.namespace
@namespace
end
def self.resolve_const(name)
build_component(name)
end
def self.resolve_method(name, *args)
if name.match(/^[A-Z]/)
if block_given?
build_component(name, *args) { gateway.instance_eval(&proc) }
else
build_component(name, *args)
end
else
args = [*args, proc] if block_given?
component_style_set(name, *args)
end
end
def self.new_component(name, *args)
namespace.const_get(name).new(*args)
end
def self.build_component(name, *args)
if block_given?
component = worker.run_with_stack(new_component(name, *args), &proc)
else
component = new_component(name, *args)
end
if worker.empty?
component
else
worker.transfer(:add, component)
end
end
def self.component_style_set(name, *args)
if (worker.acceptable?(:style_include?) and
worker.transfer(:style_include?, name))
worker.transfer(:style_set, name, *args)
elsif worker.acceptable?(:"#{name}=")
worker.transfer(:"#{name}=", *args)
else
raise unless worker.acceptable?(name)
worker.transfer(name, *args)
end
end
def self.build(container, &proc)
container = Class.new { include container } if container.class == Module
setup_build(proc)
worker.run_with_stack(container.new){
gateway.instance_eval(&proc)
}.tap { cleanup_build(proc) }
end
def self.setup_build(proc) proc.binding.eval <<-EOD end
class << self.class
alias __const_missing__ const_missing if defined? const_missing
def const_missing(name) Quincite::UI.resolve_const(name) end
end
EOD
def self.cleanup_build(proc) proc.binding.eval <<-EOD end
class << self.class
remove_method :const_missing
if defined? __const_missing__
alias const_missing __const_missing__
remove_method :__const_missing__
end
end
EOD
end
end
module Quincite
module UI
module RootContainer
def self.width
UI.max_width
end
def self.height
UI.max_height
end
end
@max_width = 0
@max_height = 0
def self.max_width
@max_width
end
def self.max_height
@max_height
end
def self.max_width=(width)
@max_width = width
end
def self.max_height=(height)
@max_height = height
end
end
end
module Quincite
module UI
module Control
def init_control
@event_handlers = {}
end
def add_event_handler(name, event_handler)
@event_handlers[name] = event_handler
end
end
end
end
module Quincite
module UI
module Container
attr_reader :components
def self.try_to_a(obj)
obj.respond_to?(:to_a) and obj.to_a
end
def init_container
@components = []
end
def add(obj)
components << obj
end
def to_a
[self, *components.map {|obj| Container.try_to_a(obj) or obj }]
end
def all_components
to_a.flatten
end
def all(order=:asc)
order == :desc ? all_components.reverse : all_components
end
def find(id)
all_components.find {|obj| obj.id == id }
end
end
end
end
module Quincite
module UI
module Component
extend Forwardable
attr_accessor :id
attr_reader :style
attr_reader :content_width, :content_height
def_delegator :@style, :position
def_delegators :@style, :top, :left, :bottom, :right
def_delegators :@style, :padding_top, :padding_bottom
def_delegators :@style, :padding_left, :padding_right
def_delegators :@style, :bg_image, :bg_color
def_delegators :@style, :border_width, :border_color
def_delegator :@style, :justify_content
def_delegator :@style, :align_items
def_delegator :@style, :layout, :layout_style
def init_component
@style = Style.new
end
def style_include?(name)
@style.respond_to?("#{name}=")
end
def style_set(name, args)
@style.__send__("#{name}=", args)
end
def width
@width || content_width || 0
end
def height
@height || content_height || 0
end
def layout_width
return 0 if position == :absolute
width + margin_left + margin_right
end
def layout_height
return 0 if position == :absolute
height + margin_top + margin_bottom
end
def margin_top
return 0 if position == :absolute
@style.margin_top
end
def margin_right
return 0 if position == :absolute
@style.margin_right
end
def margin_bottom
return 0 if position == :absolute
@style.margin_bottom
end
def margin_left
return 0 if position == :absolute
@style.margin_left
end
def horizontal_margin
margin_left + margin_right
end
def offset_left(parent)
[parent.padding_left, margin_left].max
end
def offset_right(parent)
[parent.padding_right, margin_right].max
end
def horizontal_offset(parent)
offset_left(parent) + offset_right(parent)
end
def vertical_margin
margin_top + margin_bottom
end
def offset_top(parent)
[parent.padding_top, margin_top].max
end
def offset_bottom(parent)
[parent.padding_bottom, margin_bottom].max
end
def vertical_offset(parent)
offset_top(parent) + offset_bottom(parent)
end
def inner_width(parent)
if parent.respond_to?(:padding_left) && parent.respond_to?(:padding_right)
parent.width - horizontal_offset(parent)
else
parent.width - horizontal_margin
end
end
def inner_height(parent)
if parent.respond_to?(:padding_top) && parent.respond_to?(:padding_bottom)
parent.height - vertical_offset(parent)
else
parent.height - vertical_margin
end
end
def layout(ox=0, oy=0, parent=RootContainer)
resize(parent)
move(ox, oy, parent)
end
def move(to_x, to_y, parent)
move_x(to_x, parent)
move_y(to_y, parent)
end
def move_x(to_x, parent)
if position == :absolute
if left and Numeric === left
case left
when Fixnum
self.x = to_x + left
when Float
self.x = to_x + (parent.width - self.width) * left
end
elsif right and Numeric === right
case right
when Fixnum
self.x = to_x + parent.width - self.width - right
when Float
self.x = to_x + (parent.width - self.width) * (1 - right)
end
else
self.x = to_x
end
else
if left and Numeric === left
case left
when Fixnum
self.x = to_x + left
when Float
self.x = to_x + left * self.width
end
elsif right and Numeric === right
case right
when Fixnum
self.x = to_x - right
when Float
self.x = to_x - right * self.width
end
else
self.x = to_x
end
end
end
private :move_x
def move_y(to_y, parent)
if position == :absolute
if top and Numeric === top
case top
when Fixnum
self.y = to_y + top
when Float
self.y = to_y + (parent.height - self.height) * top
end
elsif bottom and Numeric === bottom
case bottom
when Fixnum
self.y = to_y + parent.height - self.height - bottom
when Float
self.y = to_y + (parent.height - self.height) * (1 - bottom)
end
else
self.y = to_y
end
else
if top and Numeric === top
case top
when Fixnum
self.y = to_y + top
when Float
self.y = to_y + top * self.height
end
elsif bottom and Numeric === bottom
case bottom
when Fixnum
self.y = to_y - bottom
when Float
self.y = to_y - bottom * self.height
end
else
self.y = to_y
end
end
end
private :move_y
def resize(parent)
case @style.width
when Integer
@width = @style.width
when Float
@width = parent.width * @style.width
when :full
@width = inner_width(parent)
else
@width = nil
end
case @style.height
when Integer
@height = @style.height
when Float
@height = parent.height * @style.height
when :full
@height = inner_height(parent)
else
@height = nil
end
end
def break_after?
!!@style.break_after
end
def visible?
!!@style.visible
end
end
end
end
module Quincite
module UI
module Layouter
def resize(parent)
super
__send__(:"#{layout_style}_resize")
self
end
def move(ox=0, oy=0, parent)
super
__send__(:"#{layout_style}_move")
self
end
def flow_resize
v_margin = padding_top
@content_width = @width || UI.max_width
@content_height = components.lazy.each {|component|
component.resize(self)
}.slice_before(&components_overflow?).inject(0) {|height, row|
component = row.max_by(&:layout_height)
next height if component.position == :absolute
v_space = [v_margin, component.margin_top].max + height
v_margin = component.margin_bottom
v_space + component.height
} + [v_margin, padding_bottom].max
end
private :flow_resize
def flow_move
v_margin = padding_top
components.slice_before(&components_overflow?).inject(0) do |height, row|
component = row.max_by(&:layout_height)
max_component_height = component.height
v_space = [v_margin, component.margin_top].max + height
v_margin = component.margin_bottom
inner_width = row.inject(0, &row_injection) + [row.last.margin_right, padding_right].max
row.inject(0, &row_injection {|component, h_space|
x = self.x + h_space + case justify_content
when :space_between
if row.size > 1 and not row.last.break_after?
h_space += (self.width - inner_width) / (row.size - 1).to_f
end
0
when :center
(self.width - inner_width) / 2.0
when :right
self.width - inner_width
else
0
end
y = self.y + v_space + case align_items
when :center
(max_component_height - component.height) / 2.0
when :bottom
max_component_height - component.height
else
0
end
if component.position == :absolute
component.move(self.x, self.y, self)
else
component.move(x, y, self)
end
h_space
})
v_space + max_component_height
end
end
private :flow_move
def row_injection(&with)
h_margin = padding_left
-> width, component do
h_space = [h_margin, component.margin_left].max + width
h_space = with.call(component, h_space) if with
next width if component.position == :absolute
h_margin = component.margin_right
h_space + component.width
end
end
private :row_injection
def components_overflow?
h_margin = padding_top
width = 0
max_width = @content_width
force_break = false
-> component do
next false if component.position == :absolute
h_space = [h_margin, component.margin_left].max + component.width
if force_break
force_break = component.break_after?
width = h_space
h_margin = padding_left
next true
else
force_break = component.break_after?
end
expected_width = width + component.layout_width + padding_left + padding_right
if width > 0 and expected_width > max_width
width = h_space
h_margin = padding_left
true
else
width += h_space
h_margin = component.margin_right
false
end
end
end
private :components_overflow?
def vertical_box_resize
v_margin = padding_top
@content_height = components.inject(0) {|height, component|
component.resize(self)
next height if component.position == :absolute
v_space = [v_margin, component.margin_top].max + height
v_margin = component.margin_bottom
v_space + component.height
} + [v_margin, padding_bottom].max
component = components.max_by(&:layout_width)
if component
@content_width = component.width +
[component.margin_left, padding_left].max +
[component.margin_right, padding_right].max
end
end
private :vertical_box_resize
def vertical_box_move
v_margin = padding_top
components.inject(0) do |height, component|
h_space = [padding_left, component.margin_left].max
v_space = [v_margin, component.margin_top].max + height
x = self.x + h_space + case justify_content
when :center
(component.inner_width(self) - component.width) / 2
when :right
component.inner_width(self) - component.width
else
0
end
y = self.y + v_space + case align_items
when :space_between
if @height and components.size > 1
v_space += (@height - @content_height) / (components.size - 1)
end
0
when :center
@height ? (@height - @content_height) / 2 : 0
when :bottom
@height ? @height - @content_height : 0
else
0
end
if component.position == :absolute
component.move(self.x, self.y, self)
else
component.move(x, y, self)
end
next height if component.position == :absolute
v_margin = component.margin_bottom
v_space + component.height
end
end
private :vertical_box_move
def horizontal_box_resize
h_margin = padding_left
@content_width = components.inject(0) {|width, component|
component.resize(self)
next width if component.position == :absolute
h_space = [h_margin, component.margin_left].max + width
h_margin = component.margin_right
h_space + component.width
} + [h_margin, padding_right].max
component = components.max_by(&:layout_height)
if component
@content_height = component.height +
[component.margin_top, padding_top].max +
[component.margin_bottom, padding_bottom].max
end
end
private :horizontal_box_resize
def horizontal_box_move
h_margin = padding_left
components.inject(0) do |width, component|
h_space = [h_margin, component.margin_left].max + width
v_space = [padding_top, component.margin_top].max
x = self.x + h_space + case justify_content
when :space_between
if @width and components.size > 1
h_space += (@width - @content_width) / (components.size - 1)
end
0
when :center
@width ? (@width - @content_width) / 2 : 0
when :right
@width ? @width - @content_width : 0
else
0
end
y = self.y + v_space + case align_items
when :center
(component.inner_height(self) - component.height) / 2
when :bottom
component.inner_height(self) - component.height
else
0
end
if component.position == :absolute
component.move(self.x, self.y, self)
else
component.move(x, y, self)
end
next width if component.position == :absolute
h_margin = component.margin_right
h_space + component.width
end
end
private :horizontal_box_move
end
end
end
Quincite::UI.max_width = Graphics.width
Quincite::UI.max_height = Graphics.height
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment