Created
December 21, 2016 09:32
-
-
Save aoitaku/333278322eda38fa79482dbe6387a846 to your computer and use it in GitHub Desktop.
quincite_for_rgss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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? | |
[email protected]_after | |
end | |
def visible? | |
[email protected] | |
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