Langage : Ruby
Posté le 13 juillet 2016
Télécharger | Reposter
# -*- coding: utf-8 -*- #============================================================================== # ** RME Gui #------------------------------------------------------------------------------ # With : # Joke # Grim # Nuki # #------------------------------------------------------------------------------ # Graphical User Interface SDK for RME's tools #============================================================================== =begin License coming soon =end if RME.gui_enabled? #============================================================================== # ** Color #------------------------------------------------------------------------------ # The RGBA color class. #============================================================================== class Color alias_method :sdk_initialize, :initialize def initialize(*args) if args.length == 1 && args[0].is_a?(String) args = args[0][1..6].scan(/../).map{|color| color.to_i(16)} end sdk_initialize(*args) end end #============================================================================== # ** Generative #------------------------------------------------------------------------------ # Mixins collection #============================================================================== module Generative #============================================================================== # ** Stackable #------------------------------------------------------------------------------ # Dad in Mom #============================================================================== module Stackable #-------------------------------------------------------------------------- # * Pushes other in self #-------------------------------------------------------------------------- def <<(oth) oth.viewport = Viewport.new if (oth.is_a?(Sprite) && !oth.viewport) oth.parent = self self.children ||= [] self.children << oth compute oth end #-------------------------------------------------------------------------- # * Pushes self in other #-------------------------------------------------------------------------- def >>(oth) self.viewport = Viewport.new if (self.is_a?(Sprite) && !self.viewport) self.parent = oth oth.children ||= [] oth.children << self compute oth end #-------------------------------------------------------------------------- # * Parents #-------------------------------------------------------------------------- def parents return [] unless parent parents = [parent] parents << parents[-1].parent while parents[-1].parent parents end #-------------------------------------------------------------------------- # * Computing #-------------------------------------------------------------------------- def compute compute_self recompute_children end #-------------------------------------------------------------------------- # * Computes self #-------------------------------------------------------------------------- def compute_self end #-------------------------------------------------------------------------- # * Children recomputing #-------------------------------------------------------------------------- def recompute_children children.each{|c| c.compute} if self.children end #-------------------------------------------------------------------------- # * Free stacking #-------------------------------------------------------------------------- def dispose_stack children.reverse.each do |c| c.dispose if c.respond_to?(:dispose) end if self.children parent.children.delete(self) if self.parent end end #============================================================================== # ** RectComputing #------------------------------------------------------------------------------ # Baby's shape #============================================================================== module RectComputing #-------------------------------------------------------------------------- # * Computing rules #-------------------------------------------------------------------------- module Rules class << self attr_accessor :values Rules.values = Hash.new delegate_accessor :values, :[] end self[:absolute] = proc do |r| r.true_x = r.abs_x = r.x r.true_y = r.abs_y = r.y r.true_width = r.width r.true_height = r.height end self[:relative] = proc do |r| pa = r.parent.inner r.true_x = r.abs_x = r.x + pa.abs_x r.true_y = r.abs_y = r.y + pa.abs_y r.true_width = r.width r.true_height = r.height end self[:enclosed] = proc do |r| pa = r.parent.inner ax = r.abs_x = r.x + pa.abs_x ay = r.abs_y = r.y + pa.abs_y mx = ax + r.width my = ay + r.height lx = pa.true_x + pa.true_width ly = pa.true_y + pa.true_height r.true_x = ax.bound(pa.true_x, lx) r.true_y = ay.bound(pa.true_y, ly) r.true_width = mx.bound(pa.true_x, lx) - r.true_x r.true_height = my.bound(pa.true_y, ly) - r.true_y end end #-------------------------------------------------------------------------- # * Legacy of Rect #-------------------------------------------------------------------------- attr_accessor :legacy_rule #-------------------------------------------------------------------------- # * Computes real Rect from legacy rules #-------------------------------------------------------------------------- def compute_self if self.parent @legacy_rule ||= :enclosed Rules[@legacy_rule][self] else Rules[:absolute][self] end end end end #============================================================================== # ** Percentage #------------------------------------------------------------------------------ # Ugly pseudo-typage method #============================================================================== class Numeric def percent? false end def percent Percentage.new(self) end end class Percentage attr_accessor :value [:abs,:ceil,:floor,:integer?,:round,:truncate, :*,:+,:-,:/,:%,:**,:<=>,:==,:<,:<=,:>,:>=].each{|m| delegate :value, m} def initialize(value) @value = value end def percent? true end end #============================================================================== # ** Rect #------------------------------------------------------------------------------ # The rectangle class. #============================================================================== class Rect #-------------------------------------------------------------------------- # * Import Imbrication API #-------------------------------------------------------------------------- include Generative::Stackable include Generative::RectComputing #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :children, :parent, :abs_x, :abs_y #-------------------------------------------------------------------------- # * Alias #-------------------------------------------------------------------------- alias_method :true_x, :x alias_method :true_y, :y alias_method :true_x=, :x= alias_method :true_y=, :y= alias_method :true_width, :width alias_method :true_height, :height alias_method :true_width=, :width= alias_method :true_height=, :height= #-------------------------------------------------------------------------- # * Auto-computing #-------------------------------------------------------------------------- [ :x, :y, :width, :height ].each{|m| attr_accessor_callback :compute, m} #-------------------------------------------------------------------------- # * Gets properties #-------------------------------------------------------------------------- def abs_x; @abs_x ||= true_x; end def abs_y; @abs_y ||= true_y; end def x; @x ||= true_x; end def y; @y ||= true_y; end def width; @width ||= true_width; end def height; @height ||= true_height; end #-------------------------------------------------------------------------- # * Sets all parameters at once #-------------------------------------------------------------------------- def set(*args) if (a = args[0]).is_a? Rect @x, @y, @width, @height = a.x, a.y, a.width, a.height else @x, @y, @width, @height = *args end compute self end #-------------------------------------------------------------------------- # * Sets all components to 0. #-------------------------------------------------------------------------- def empty @x = @y = @width = @height = self.true_x = self.true_y = self.true_width = self.true_height = 0 self end #-------------------------------------------------------------------------- # * Gets real Rect computed from legacy rules #-------------------------------------------------------------------------- def computed Rect.new(true_x, true_y, true_width, true_height) end #-------------------------------------------------------------------------- # * Clone #-------------------------------------------------------------------------- def clone Rect.new(*parameters) end alias :dup :clone #-------------------------------------------------------------------------- # * Returns parameters into Array format #-------------------------------------------------------------------------- def parameters [self.x, self.y, self.width, self.height] end #-------------------------------------------------------------------------- # * Sets parameters without auto-computing #-------------------------------------------------------------------------- def set_parameters(x, y, w, h) @x = x @y = y @width = w @height = h end #-------------------------------------------------------------------------- # * check if point 's include in the rect #-------------------------------------------------------------------------- def in?(*p) point = p.to_point point.in?(self.computed) end #-------------------------------------------------------------------------- # * Inception #-------------------------------------------------------------------------- def inner self end end #============================================================================== # ** Viewport #------------------------------------------------------------------------------ # Used when displaying sprites on one portion of the true #============================================================================== class Viewport #-------------------------------------------------------------------------- # * Import Imbrication API #-------------------------------------------------------------------------- include Generative::Stackable #-------------------------------------------------------------------------- # * Alias #-------------------------------------------------------------------------- alias_method :true_z, :z alias_method :true_z=, :z= alias_method :true_ox, :ox alias_method :true_ox=, :ox= alias_method :true_oy, :oy alias_method :true_oy=, :oy= alias_method :inner, :rect alias_method :sdk_dispose, :dispose #-------------------------------------------------------------------------- # * Delegation #-------------------------------------------------------------------------- [ :children, :parent, :legacy_rule ].each{|m| delegate_accessor :rect, m} #-------------------------------------------------------------------------- # * Auto-computing when changing parameters #-------------------------------------------------------------------------- [ :ox, :oy, :x, :y, :width, :height ].each{|m| attr_accessor_callback :compute, m} #-------------------------------------------------------------------------- # * Gets properties #-------------------------------------------------------------------------- def ox; @ox ||= true_ox; end def oy; @oy ||= true_oy; end def x; @x ||= rect.x; end def y; @y ||= rect.y; end def z; @z ||= true_z; end def width; @width ||= rect.width; end def height; @height ||= rect.height; end #-------------------------------------------------------------------------- # * The viewport's z-coordinate. #-------------------------------------------------------------------------- def z=(v) if @z != v @z = v compute_z end end #-------------------------------------------------------------------------- # * Sets parameters without auto-computing #-------------------------------------------------------------------------- def set_parameters(x, y, w, h) @x = x @y = y @width = w @height = h end #-------------------------------------------------------------------------- # * Computes real Rect from legacy rules #-------------------------------------------------------------------------- def compute_self rect.set(self.x, self.y, self.width, self.height) rect.compute_self self.true_ox = rect.true_x - rect.abs_x + self.ox self.true_oy = rect.true_y - rect.abs_y + self.oy compute_z end #-------------------------------------------------------------------------- # * Computes real z-coordinate from legacy rule #-------------------------------------------------------------------------- def compute_z if self.parent && self.parent.respond_to?(:z) self.true_z = self.z + self.parent.true_z + 1 else self.true_z = self.z end if self.children self.children.each{|c| c.respond_to?(:compute_z) && c.compute_z} end end #-------------------------------------------------------------------------- # * Dispose #-------------------------------------------------------------------------- def dispose @disposed = true dispose_stack sdk_dispose end attr_reader :disposed alias_method :disposed?, :disposed end #============================================================================== # ** Sprite #------------------------------------------------------------------------------ # Append Stackable Component #============================================================================== class Sprite #-------------------------------------------------------------------------- # * Delegation #-------------------------------------------------------------------------- [ :children, :parent, :legacy_rule, :compute ].each{|m| delegate_accessor :viewport, m} #-------------------------------------------------------------------------- # * Import Imbrication API #-------------------------------------------------------------------------- include Generative::Stackable end #============================================================================== # ** GUI #------------------------------------------------------------------------------ # Graphical User interface #============================================================================== module Gui #============================================================================== # ** Style #------------------------------------------------------------------------------ # Telling what the things looks like #============================================================================== class Style #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :values #-------------------------------------------------------------------------- # * Style initialize #-------------------------------------------------------------------------- def initialize(args = nil) @values = Hash.new set background_color: Color.new(*[255]*3), border_color: Color.new(*[0]*3), x: 0, y: 0, width: :auto, height: :auto, border: 1, border_width: 1, border_radius: 0, font: get_profile("small_standard").to_font, max_height: nil, min_height: 0, max_width: nil, min_width: 0, padding: 8, margin: 0, value: " ", title: " ", checked_label: "x", unchecked_label: " ", display: :inline set args if args end #-------------------------------------------------------------------------- # * Reading & Writing access to style specs #-------------------------------------------------------------------------- def [](k) @values[k.to_sym] end def []=(k,v) @values[k.to_sym] = v end #-------------------------------------------------------------------------- # * Set style spec with named args (ex: set(margin: 5)) #-------------------------------------------------------------------------- def set(args) return unless args args.each do |k,v| if [:margin, :padding, :border].include?(k) set_param(k, *v) else self[k] = v end end self end #-------------------------------------------------------------------------- # * Quick style specification for :margin, :padding, :border # Exemple : # border: 5 => all border is set to 5 # border: [5,8] => top&bottom border is set to 5 # left&right border is set to 8 # border: [5,8,10] => in order: top, left&right, bottom definition # border: [5,8,10,3] => in order: top, right, bottom, left definition #-------------------------------------------------------------------------- def set_param(m, *args) m = m.to_s case args.length when 1 self["#{m}_bottom"] = self["#{m}_top"] = self["#{m}_left"] = self["#{m}_right"] = args[0] when 2 self["#{m}_bottom"] = self["#{m}_top"] = args[0] self["#{m}_left"] = self["#{m}_right"] = args[1] when 3 self["#{m}_top"] = args[0] self["#{m}_bottom"] = args[2] self["#{m}_left"] = self["#{m}_right"] = args[1] else self["#{m}_top"], self["#{m}_right"], self["#{m}_bottom"], self["#{m}_left"] = *args end end #-------------------------------------------------------------------------- # * Contracts the given rect with the given method #-------------------------------------------------------------------------- def contract_with(m, r) m = m.to_s x = r.x + self["#{m}_left"] y = r.y + self["#{m}_top" ] w = r.width - self["#{m}_left"] - self["#{m}_right" ] h = r.height - self["#{m}_top" ] - self["#{m}_bottom"] r.set x, y, w, h end #-------------------------------------------------------------------------- # * CSS application #-------------------------------------------------------------------------- def css_match(obj) Gui::CSS.apply_to(obj) end end #============================================================================== # ** CSS #------------------------------------------------------------------------------ # Telling what the things looks like LEVEL 2 #============================================================================== module CSS #-------------------------------------------------------------------------- # * Singleton #-------------------------------------------------------------------------- class << self #-------------------------------------------------------------------------- # * CSS entries accessor #-------------------------------------------------------------------------- attr_accessor :entries delegate_accessor :entries, :[] CSS.entries = Hash.new #-------------------------------------------------------------------------- # * Matching and application for Object's Style instance #-------------------------------------------------------------------------- def apply_to(obj) entries.each do |selector, style| obj.style.set(style) if selector[obj] end end end # End Singleton #-------------------------------------------------------------------------- # * CSS writing method #-------------------------------------------------------------------------- def set(*selectors, style) selectors.each do |s| s = s+'._' unless s.include?('.') s = '_ '+s unless s.include?(' ') s = s.split(/\ |\./) sl = proc do |obj| c1 = s[0] == "_" || obj.parents.any?{|pa| pa.class.to_s == s[0]} c2 = s[1] == "" || obj.class.to_s == s[1] c3 = s[2] == "_" || obj.respond_to?(:name) && obj.name.to_s == s[2] c1 && c2 && c3 end CSS[sl] = style end end end #============================================================================== # ** GUI::Tools #------------------------------------------------------------------------------ # Graphical User interface helpers #============================================================================== module Tools class << self def random_color Color.new(rand(255), rand(255), rand(255)) end def password_char '•' end end module Activable def actived? @active end def activate @active = true end def deactivate @active = false end def trigger_activation @active = !@active end def activation_callback @activation_callback end def set_activation_callback(&block) @activation_callback = lambda{|s| false} @activation_callback = block if block_given? end def check_callback if @activation_callback if @activation_callback.call(self) @active = true end end end end end #============================================================================== # ** GUI::Components #------------------------------------------------------------------------------ # Graphical User interface components #============================================================================== module Components #============================================================================== # ** Text_Field #------------------------------------------------------------------------------ # IZI Things #============================================================================== class Text_Field #-------------------------------------------------------------------------- # * IZI Public instance variables #-------------------------------------------------------------------------- [ :in?, :hover?, :click?, :press?, :trigger?, :repeat?, :release?, :mouse_x, :mouse_y, :>>, :<< ].each{|m| delegate :@viewport, m} attr_reader :cursor delegate_accessor :@text, :value delegate_accessor :@text, :virtual_position delegate_accessor :@text, :selection_start delegate :@text, :formatted_value delegate :@text, :has_transformation? delegate :@text, :delete [:x, :y, :width, :height, :inner, :outer].each{|m| delegate_accessor :@viewport, m} include Tools::Activable #-------------------------------------------------------------------------- # * IZI Object initialize #-------------------------------------------------------------------------- def initialize(textrecorder,x,y,w,font="Standard",active=false,&block) @active = active @text = textrecorder @x,@y,@w = x,y,w if font.is_a?(Font) @font = font else @font = get_profile(font).to_font end @text.start_capture @cursor_timer = 0 create_sprite create_viewport create_selection_rect create_cursor set_activation_callback(&block) update_bitmap update_cursor_pos end #-------------------------------------------------------------------------- # * Refresh #-------------------------------------------------------------------------- def refresh update_bitmap update_cursor_pos update_selection_rect update_viewport end #-------------------------------------------------------------------------- # * IZI Update #-------------------------------------------------------------------------- def update unless actived? @cursor.opacity = 0 @selection_rect.opacity = 0 check_callback else @selection_rect.opacity = 255 locate if press?(:mouse_left) || Mouse.dragging? && in?(Mouse.drag.start) @text.update unless Devices::Keys::Enter.press? || Devices::Keys::Tab.press? update_bitmap if @text.has_transformation? if @text.cursor_has_moved? update_cursor_pos update_selection_rect update_viewport end update_cursor_blink end end #-------------------------------------------------------------------------- # * IZI Sprite creation #-------------------------------------------------------------------------- def create_sprite @sprite = Sprite.new @sprite.bitmap = Bitmap.new(1,1) @sprite.bitmap.font = @font @split_format = 640 / @sprite.bitmap.text_size("W").width end #-------------------------------------------------------------------------- # * IZI Viewport creation #-------------------------------------------------------------------------- def create_viewport @h = @sprite.bitmap.text_size("W").height @viewport = Viewport.new(@x,@y,@w,@h) @sprite.viewport = @viewport end #-------------------------------------------------------------------------- # * IZI Cursor creation #-------------------------------------------------------------------------- def create_cursor @cursor = Sprite.new(@viewport) @cursor.bitmap = Bitmap.new(1,@h) @cursor.bitmap.fill_rect(0,0,1,@h,Color.new(0,0,0)) @cursor.ox = 1 end def create_selection_rect @selection_rect = Sprite.new(@viewport) @selection_rect.bitmap = Bitmap.new(1,1) @selection_rect.bitmap.fill_rect(0,0,1,1,Color.new(51,153,255)) @selection_rect.zoom_x = 0 @selection_rect.zoom_y = @h @selection_rect.z = -1 end #-------------------------------------------------------------------------- # * IZI Bitmap update #-------------------------------------------------------------------------- def update_bitmap text = value text = " " if text.empty? rect = @sprite.bitmap.text_size(text) @sprite.bitmap.dispose @sprite.bitmap = Bitmap.new(rect.width, rect.height) @sprite.bitmap.font = @font last_x = 0 text.split_each(@split_format).each do |a_text| rect = @sprite.bitmap.text_size(a_text) rect.x = last_x @sprite.bitmap.draw_text(rect, a_text) last_x += rect.width end end #-------------------------------------------------------------------------- # * IZI Cursor blink #-------------------------------------------------------------------------- def update_cursor_blink @cursor_timer = (@cursor_timer + 1/20.0)%2 @cursor.opacity = @cursor_timer < 1 ? 255 : 0 end #-------------------------------------------------------------------------- # * IZI Cursor position update #-------------------------------------------------------------------------- def update_cursor_pos @cursor_timer = 0 pos = @text.virtual_position if pos == 0 @cursor.x = 1 else @cursor.x = @sprite.bitmap.text_size(value[0...pos]).width end end #-------------------------------------------------------------------------- # * IZI Selection Rect update #-------------------------------------------------------------------------- def update_selection_rect pos = @text.selection_start if pos == 0 @selection_rect.x = 1 else @selection_rect.x = @sprite.bitmap.text_size(value[0...pos]).width end delta = @cursor.x - @selection_rect.x @selection_rect.zoom_x = delta.abs @selection_rect.x += delta if delta < 0 end #-------------------------------------------------------------------------- # * IZI Viewport update #-------------------------------------------------------------------------- def update_viewport a = @cursor.x - self.width b = @sprite.bitmap.width - self.width @viewport.ox = @viewport.ox.bound(a, b).bound(0, @cursor.x-1) end #-------------------------------------------------------------------------- # * IZI Cursor location on click #-------------------------------------------------------------------------- def locate x = mouse_x + @viewport.ox approx = x * value.length / @sprite.bitmap.width match = approach(approx, x) @text.cursor_jump(match) @viewport.ox -= 10 if mouse_x < 0 @viewport.ox += 10 if (self.width - mouse_x) < 0 end #-------------------------------------------------------------------------- # * IZI Approach #-------------------------------------------------------------------------- def approach(a, x, memoa=a, memob=0) bound = a.bound(0,value.length) return bound if bound != a b = @sprite.bitmap.text_size(value[0...a]).width return a if (b-x) == 0 || (b-x)==(x-memob) return memoa if (b-x).abs > (memob-x).abs approach(a + (0 <=> (b-x)), x, a, b) end #-------------------------------------------------------------------------- # * IZI Dispose #-------------------------------------------------------------------------- def dispose self.instance_variables.each{|i| self.instance_variable_set(i, nil) } end end #============================================================================== # ** TextRecorder #------------------------------------------------------------------------------ # Record text state #============================================================================== class Abstract_Recorder #-------------------------------------------------------------------------- # * Public instance variables #-------------------------------------------------------------------------- attr_accessor :exit_keys attr_accessor :stopped attr_accessor :transformed attr_accessor :cursor_moved attr_accessor :virtual_position attr_accessor :selection_start alias_method :stopped?, :stopped def value=(v); @value = v; end def value; @value; end def formatted_value; @value; end #-------------------------------------------------------------------------- # * Has a transformation #-------------------------------------------------------------------------- def has_transformation? result = @transformed @transformed = false result end #-------------------------------------------------------------------------- # * Cursor has moved #-------------------------------------------------------------------------- def cursor_has_moved? result = @cursor_moved @cursor_moved = false result end #-------------------------------------------------------------------------- # * Cursor jump #-------------------------------------------------------------------------- def cursor_jump(v) @virtual_position = v bound_cursor end #-------------------------------------------------------------------------- # * Start capture #-------------------------------------------------------------------------- def start_capture @stopped = false update end #-------------------------------------------------------------------------- # * Sop capture #-------------------------------------------------------------------------- def stop_capture @stopped = true end #-------------------------------------------------------------------------- # * Bound cursor position #-------------------------------------------------------------------------- def bound_cursor @virtual_position = @virtual_position.bound(0, @value.length) @cursor_moved = true c = [nil, ''].include?(Keyboard.current_char) @selection_start = @virtual_position unless (Keyboard.shift && c || Mouse.dragging?) end #-------------------------------------------------------------------------- # * go left #-------------------------------------------------------------------------- def go_left @virtual_position -= 1 bound_cursor end #-------------------------------------------------------------------------- # * go Right #-------------------------------------------------------------------------- def go_right @virtual_position += 1 bound_cursor end #-------------------------------------------------------------------------- # * Update key capture #-------------------------------------------------------------------------- def update return if stopped? return stop_capture if @exit_keys.any?{|key| Keyboard.repeat?(key)} update_virtual_cursor update_value_modification update_value update_clipboard end #-------------------------------------------------------------------------- # * Running #-------------------------------------------------------------------------- def recording? !stopped? end #-------------------------------------------------------------------------- # * Update text content #-------------------------------------------------------------------------- def update_value return if @limit && @value.length >= @limit c = Keyboard.current_char unless [nil, ''].include?(c) delete(0) @value = @value.insert_at(@virtual_position, c) @transformed = true go_right end end #-------------------------------------------------------------------------- # * Delete between #-------------------------------------------------------------------------- def delete(dir=-1) range = [@virtual_position, @selection_start].sort if range[0] == range[1] return if @virtual_position == 0 range[0] -= 1 if dir == -1 range[1] += 1 if dir == 1 end @value = @value.delete_between(*range) @virtual_position = @selection_start = range[0] @cursor_moved = @transformed = true end #-------------------------------------------------------------------------- # * Update text content (bcps and del) #-------------------------------------------------------------------------- def update_value_modification if Keyboard.krepeat?(0x08) delete end if Keyboard.krepeat?(0x2E) delete(1) end end #-------------------------------------------------------------------------- # * Update cursor position #-------------------------------------------------------------------------- def update_virtual_cursor go_left if Keyboard.krepeat?(0x25) go_right if Keyboard.krepeat?(0x27) end #-------------------------------------------------------------------------- # * Update clipboard #-------------------------------------------------------------------------- def update_clipboard if Keyboard.ctrl?(:c) c = value[Range.new(*[@selection_start, @virtual_position].sort,true)] Clipboard.push_text(c) unless c.empty? end if Keyboard.ctrl?(:x) c = value[Range.new(*[@selection_start, @virtual_position].sort,true)] Clipboard.push_text(c) unless c.empty? delete(0) end if Keyboard.ctrl?(:v) delete(0) c = Clipboard.get_text @value = @value.insert_at(@virtual_position, c) @virtual_position += c.length @transformed = true bound_cursor end if Keyboard.ctrl?(:a) @selection_start = 0 @virtual_position = value.length @cursor_moved = true end end end #============================================================================== # ** TextRecorder #------------------------------------------------------------------------------ # Record text state #============================================================================== class Text_Recorder < Abstract_Recorder #-------------------------------------------------------------------------- # * Object initialize #-------------------------------------------------------------------------- def initialize(init = "", limit = nil , exit_keys = []) @transformed = true @stopped = true @exit_keys = exit_keys @limit = limit @value = init @value = init[0...@limit] if @limit @virtual_position = @selection_start = init.length end end #============================================================================== # ** Int_Recorder #------------------------------------------------------------------------------ # Record int state #============================================================================== class Int_Recorder < Abstract_Recorder #-------------------------------------------------------------------------- # * Object initialize #-------------------------------------------------------------------------- def initialize(init = 0, limit = nil , exit_keys = []) @transformed = true @stopped = true @exit_keys = exit_keys @limit = limit @value = init @value = init.bound(@limit.min, @limit.max) if @limit @value = @value.to_s @virtual_position = @selection_start = @value.length end #-------------------------------------------------------------------------- # * Value accessor #-------------------------------------------------------------------------- def formatted_value; self.value=@value @value.to_i end def value; @value; end def value=(v) return @value = v if v == '+' || v == '-' @value = v.to_i @value = @value.bound(@limit.min, @limit.max) if @limit @value = @value.to_s @virtual_position = @selection_start = @value.length end #-------------------------------------------------------------------------- # * Update text content #-------------------------------------------------------------------------- def update_value c = Keyboard.current_char unless [nil, ''].include?(c) delete(0) return unless (["+","-"] + ("0".."9").to_a).include?(c) return if @value != "" && ["+","-"].include?(c) self.value = @value.insert_at(@virtual_position, c) @transformed = true go_right end end end #============================================================================== # ** Int_Recorder #------------------------------------------------------------------------------ # Record int state #============================================================================== class Float_Recorder < Abstract_Recorder #-------------------------------------------------------------------------- # * Object initialize #-------------------------------------------------------------------------- def initialize(init = 0.0, limit = nil , exit_keys = []) @transformed = true @stopped = true @exit_keys = exit_keys @limit = limit @value = init @value = [[@limit.min,init].max, @limit.max].min if @limit @value = @value.to_s @virtual_position = @selection_start = @value.length end #-------------------------------------------------------------------------- # * Value accessor #-------------------------------------------------------------------------- def formatted_value; self.value=@value @value.to_f end def value; @value; end def value=(v) return @value = v if v == '+' || v == '-' || v == '.' @value = v.to_s if @limit result = @value.to_f unless @limit.member?(result) @value = [[@limit.min,result].max, @limit.max].min.to_s end end @virtual_position = @selection_start = @value.to_s.length end #-------------------------------------------------------------------------- # * Update text content #-------------------------------------------------------------------------- def update_value c = Keyboard.current_char unless [nil, ''].include?(c) delete(0) return unless (["+","-",'.'] + ("0".."9").to_a).include?(c) return if @value != "" && ["+","-"].include?(c) return if c == "." && @value.count(".") == 1 self.value = @value.insert_at(@virtual_position, c) @transformed = true go_right end end end end #============================================================================== # ** Gui::Base #------------------------------------------------------------------------------ # Basic Gui Component behaviour #============================================================================== class Base #-------------------------------------------------------------------------- # * Import Stackable API #-------------------------------------------------------------------------- include Generative::Stackable #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :inner, :style, :viewport, :name, :transformed alias_method :outer, :viewport [ :in?, :hover?, :click?, :press?, :trigger?, :repeat?, :release?, :mouse_x, :mouse_y, :x, :y, :width, :height, :true_z ].each{|m| delegate :viewport, m} [ :children, :parent, :legacy_rule, :z ].each{|m| delegate_accessor :viewport, m} #-------------------------------------------------------------------------- # * Object initialize # * optionnal named args = # name: String reference for the CSS matching (like 'class' in HTML) # parent: Object reference used to stack into # position: :enclosed into parent by default, can be :relative or :absolute # etc: see CSS #-------------------------------------------------------------------------- def initialize(args=nil) Interactive << self @viewport = Viewport.new(0,0,0,0) @inner = Rect.new @inner >> @viewport @name = args.delete(:name) if args && args[:name] set_parent(args.delete(:parent)) if args && args[:parent] @style = Style.new @style.css_match(self) set(args, false) initialize_intern_components compute end #-------------------------------------------------------------------------- # * initialize_intern_components #-------------------------------------------------------------------------- def initialize_intern_components end #-------------------------------------------------------------------------- # * Set value to any named args, except :name (reserved to initialize) # * :parent can't be redefined twice #-------------------------------------------------------------------------- def set(args, auto_compute=true) set_parent(args.delete(:parent)) if args && args[:parent] @style.set args self.legacy_rule = @style[:position] compute if auto_compute end #-------------------------------------------------------------------------- # * Basic parameters #-------------------------------------------------------------------------- def x=(v); style_set(:x, v); end def y=(v); style_set(:y, v); end def z=(v); style_set(:z, v); end def width=(v) ; style_set(:width, v); end def height=(v); style_set(:height, v); end def style_set(m,v) if @style[m] != v @style[m] = v compute end end def parameters [self.x, self.y, self.width, self.height] end #-------------------------------------------------------------------------- # * Push into another Object without computing #-------------------------------------------------------------------------- def set_parent(pa) self.parent = pa pa.children ||= [] pa.children << self end #-------------------------------------------------------------------------- # * Update inner #-------------------------------------------------------------------------- def update_inner @inner.set(0, 0, self.width, self.height) end #-------------------------------------------------------------------------- # * Computing #-------------------------------------------------------------------------- def compute @transformed = false compute_self if @transformed parent_style_auto? ? parent.compute : recompute_children end end #-------------------------------------------------------------------------- # * Computes self #-------------------------------------------------------------------------- def compute_self if (a=[convert(:x), convert(:y), convert(:width), convert(:height)]) != parameters @transformed = true viewport.set_parameters(*a) end viewport.z = @style[:z] viewport.compute_self end #-------------------------------------------------------------------------- # * Conversion percent to value #-------------------------------------------------------------------------- def convert(m) return auto(m) if @style[m] == :auto return @style[m] unless @style[m].percent? parent = self.parent || Viewport.new return (@style[m] * parent.inner.width / 100.0).round if [:x, :width].include?(m) return (@style[m] * parent.inner.height / 100.0).round end #-------------------------------------------------------------------------- # * Auto adjustment with children #-------------------------------------------------------------------------- def auto(m) a = children - [self.inner, self.outer] return (@style[:margin_left] + @style[:margin_right] + @style[:border_left] + @style[:border_right] + @style[:padding_left] + @style[:padding_right] + (a.delete_if{|c| c.respond_to?(:style) && (c = c.style[:width]) != :auto && c.percent? }.map{|c| c.x + c.width }.max||0) ) if m == :width return (@style[:margin_top] + @style[:margin_bottom] + @style[:border_top] + @style[:border_bottom] + @style[:padding_top] + @style[:padding_bottom] + (a.delete_if{|c| c.respond_to?(:style) && (c = c.style[:height]) != :auto && c.percent? }.map{|c| c.y + c.height}.max||0) ) if m == :height end def parent_style_auto? parent.respond_to?(:style) && [parent.style[:width], parent.style[:height]].include?(:auto) end #-------------------------------------------------------------------------- # * Interactive behaviour #-------------------------------------------------------------------------- def on_mouse_hover; on_mouse(:hover); end def on_mouse_trigger; on_mouse(:trigger); end def on_mouse_release; on_mouse(:release); end def on_mouse_click; on_mouse(:click); end def on_mouse(k) @style[k].call if @style[k].is_a? Proc end #-------------------------------------------------------------------------- # * Clone #-------------------------------------------------------------------------- def clone self.class.new(clone_properties) end #-------------------------------------------------------------------------- # * Properties #-------------------------------------------------------------------------- def clone_properties args = @style.values args[:name] = @name args[:parent] = self.parent args end #-------------------------------------------------------------------------- # * Free #-------------------------------------------------------------------------- def dispose return if @disposed @disposed = true Interactive.objects.delete(self) dispose_stack @viewport.dispose end attr_reader :disposed alias_method :disposed?, :disposed end #============================================================================== # ** Gui::Box #------------------------------------------------------------------------------ # Simple box #============================================================================== class Box < Base #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :background, :border, :outer [ :in?, :hover?, :click?, :press?, :trigger?, :repeat?, :release?, :mouse_x, :mouse_y ].each{|m| delegate :outer, m} #-------------------------------------------------------------------------- # * initialize_intern_components #-------------------------------------------------------------------------- def initialize_intern_components @border = Sprite.new(@viewport) @border.bitmap = Bitmap.new(1,1) @background = Sprite.new(@viewport) @background.bitmap = Bitmap.new(1,1) @outer = Rect.new @outer >> @viewport end #-------------------------------------------------------------------------- # * Computes self #-------------------------------------------------------------------------- def compute_self super update_colors update_background end #-------------------------------------------------------------------------- # * Update background display #-------------------------------------------------------------------------- def update_background if @style[:background] == :none || [self.width, self.height].include?(0) return update_inner end r = Rect.new(0, 0, self.width, self.height) @style.contract_with(:margin, r) fit_sprite(@border, r) @outer.set(r) @style.contract_with(:border, r) fit_sprite(@background, r) @style.contract_with(:padding, r) @inner.set(r) end #-------------------------------------------------------------------------- # * Fit sprite(1x1) into the rect #-------------------------------------------------------------------------- def fit_sprite(s, r) s.x, s.y = r.x, r.y s.zoom_x, s.zoom_y = r.width, r.height end #-------------------------------------------------------------------------- # * Update colors #-------------------------------------------------------------------------- def update_colors @border.bitmap.set_pixel(0, 0, @style[:border_color]) @background.bitmap.set_pixel(0, 0, @style[:background_color]) end #-------------------------------------------------------------------------- # * Free #-------------------------------------------------------------------------- def dispose @border.bitmap.dispose @border.dispose @background.bitmap.dispose @background.dispose super end end #============================================================================== # ** Gui::Label #------------------------------------------------------------------------------ # Label #============================================================================== class Label < Base #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :sprite_text #-------------------------------------------------------------------------- # * Object initialize # * optionnal named args = font:, value:, x:, y: #-------------------------------------------------------------------------- def initialize_intern_components @sprite_text = Sprite.new(@viewport) initialize_text(@style[:value]) end #-------------------------------------------------------------------------- # * Initialize text #-------------------------------------------------------------------------- def initialize_text(txt) return unless @sprite_text txt ||= "" fon = @style[:font] bmp = Bitmap.new(1,1) bmp.font = fon size = bmp.text_size(txt) @sprite_text.bitmap = Bitmap.new(size.width, size.height) @sprite_text.bitmap.font = fon @sprite_text.bitmap.draw_text(size, txt) @style[:width] = size.width @style[:height] = size.height end #-------------------------------------------------------------------------- # * Set value to any named args, except :name (reserved to initialize) # * :parent can't be redefined twice #-------------------------------------------------------------------------- def set(args, auto_compute=true) initialize_text(args[:value]) if args && args[:value] super(args, auto_compute) end #-------------------------------------------------------------------------- # * Value #-------------------------------------------------------------------------- def value; @style[:value]; end def value=(v) if @style[:value] != v @style[:value] = v initialize_text(v) compute end end #-------------------------------------------------------------------------- # * Value #-------------------------------------------------------------------------- def font; @style[:font]; end def font=(v) if @style[:font] != v @style[:font] = v initialize_text(@style[:value]) compute end end #-------------------------------------------------------------------------- # * Free #-------------------------------------------------------------------------- def dispose @sprite_text.bitmap.dispose @sprite_text.dispose super end end #============================================================================== # ** Gui::TrackBar #------------------------------------------------------------------------------ # Horizontal Trackbar #============================================================================== class TrackBar < Box #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :track, :bar #-------------------------------------------------------------------------- # * Object initialize # * optionnal named args = max_value:, value:, x:, y:, width: #-------------------------------------------------------------------------- def initialize(args=nil) super(args) @track = Box.new(name:'track', parent:self) @bar = Box.new(name:'bar', parent:self) Draggable << @bar update_drag_restriction end #-------------------------------------------------------------------------- # * Responsive computing #-------------------------------------------------------------------------- def compute return super unless @bar v = value super self.value = v update_drag_restriction end #-------------------------------------------------------------------------- # * Shortcuts #-------------------------------------------------------------------------- def start; @track.inner.x; end def course; @track.inner.width - @bar.width; end def max_value; @style[:max_value] || 255; end def value; max_value.to_f * (@bar.x-start) / course; end #-------------------------------------------------------------------------- # * Update drag restriction #-------------------------------------------------------------------------- def update_drag_restriction @bar.drag_restriction = Rect.new(start, @bar.y, course, 0) end #-------------------------------------------------------------------------- # * Move bar to value #-------------------------------------------------------------------------- def value=(v) @bar.x = start + course.to_f * v.fbound(0, max_value) / max_value end #-------------------------------------------------------------------------- # * Fit course to @max_value #-------------------------------------------------------------------------- def fit_to_max self.width = self.width - @track.inner.width + @bar.width + max_value end #-------------------------------------------------------------------------- # * Free #-------------------------------------------------------------------------- def dispose Draggable.objects.delete(@bar) super end end #============================================================================== # ** Gui::VerticalTrackBar #------------------------------------------------------------------------------ # Vertical Trackbar #============================================================================== class VerticalTrackBar < TrackBar #-------------------------------------------------------------------------- # * Shortcuts #-------------------------------------------------------------------------- def start; @track.inner.y; end def course; @track.inner.height - @bar.height; end def value; max_value.to_f * (@bar.y-start) / course; end #-------------------------------------------------------------------------- # * Move bar to value #-------------------------------------------------------------------------- def value=(v) @bar.y = start + course.to_f * v.fbound(0, max_value) / max_value end #-------------------------------------------------------------------------- # * Fit course to @max_value #-------------------------------------------------------------------------- def fit_to_max self.height = self.height - @track.inner.height + @bar.height + max_value end #-------------------------------------------------------------------------- # * Update drag restriction #-------------------------------------------------------------------------- def update_drag_restriction @bar.drag_restriction = Rect.new(@bar.x, start, 0, course) end end #============================================================================== # ** Gui::ScrollBar #------------------------------------------------------------------------------ # Horizontal ScrollBar #============================================================================== class ScrollBar < TrackBar; ;end #============================================================================== # ** Gui::VerticalScrollBar #------------------------------------------------------------------------------ # Vertical ScrollBar #============================================================================== class VerticalScrollBar < VerticalTrackBar; ;end #============================================================================== # ** Gui::Pannel #------------------------------------------------------------------------------ # Pannel #============================================================================== class Pannel < Box #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :title_label #-------------------------------------------------------------------------- # * Object initialize # * optionnal named args = title:, x:, y:, width:, height: + basic CSS #-------------------------------------------------------------------------- def initialize(args=nil) super(args) @title_label = Label.new( name: 'pannel_title', parent: self.outer, value: @style[:title] ) end #-------------------------------------------------------------------------- # * Set value to any named argument # * :parent can't be redefined twice #-------------------------------------------------------------------------- def set(args, ac=true) super(args, ac) @title_label.value = @style[:title] if @title_label end #-------------------------------------------------------------------------- # * Title #-------------------------------------------------------------------------- def title; @style[:title]; end def title=(v) set(title: v) end #-------------------------------------------------------------------------- # * Free #-------------------------------------------------------------------------- def dispose @title_label.dispose super end end #============================================================================== # ** Gui::Button #------------------------------------------------------------------------------ # Button #============================================================================== class Button < Box #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :title_label #-------------------------------------------------------------------------- # * Object initialize # * optionnal named args = title:, x:, y:, width:, height: + basic CSS #-------------------------------------------------------------------------- def initialize_intern_components super @title_label = Label.new( name: 'button_title', parent: self, value: @style[:title], font: @style[:font] ) Interactive.objects.delete(@title_label) end #-------------------------------------------------------------------------- # * Set value to any named argument # * :parent can't be redefined twice #-------------------------------------------------------------------------- def set(args, ac=true) super(args, ac) @title_label.value = @style[:title] if @title_label end #-------------------------------------------------------------------------- # * Title #-------------------------------------------------------------------------- def title; @style[:title]; end def title=(v) set(title: v) end #-------------------------------------------------------------------------- # * Computing #-------------------------------------------------------------------------- def compute super @title_label.set( x: (self.inner.width - @title_label.width)/2, y: (self.inner.height - @title_label.height)/2 ) if @title_label end end #============================================================================== # ** Gui::CheckBox #------------------------------------------------------------------------------ # CheckBox #============================================================================== class CheckBox < Button #-------------------------------------------------------------------------- # * Initialize #-------------------------------------------------------------------------- def initialize_intern_components @style[:title] = @style[:unchecked_label] super end #-------------------------------------------------------------------------- # * Macros #-------------------------------------------------------------------------- def checked? ; @style[:title] == @style[:checked_label] ; end def check ; self.title = @style[:checked_label] ; end def uncheck ; self.title = @style[:unchecked_label] ; end #-------------------------------------------------------------------------- # * Mouse click #-------------------------------------------------------------------------- def on_mouse_click super checked? ? uncheck : check end end #============================================================================== # ** Gui::TextField #------------------------------------------------------------------------------ # TextField #============================================================================== class TextField < Box #-------------------------------------------------------------------------- # * Public instances variables #-------------------------------------------------------------------------- attr_accessor :recorder, :textfield delegate :@textfield, :activate delegate :@textfield, :deactivate delegate :@textfield, :formatted_value #-------------------------------------------------------------------------- # * Object initialize #-------------------------------------------------------------------------- def initialize_intern_components super case @style[:format] when :int @recorder = Components::Int_Recorder.new when :float @recorder = Components::Float_Recorder.new else @recorder = Components::Text_Recorder.new end @textfield = Components::Text_Field.new(@recorder, 0, 0, 0, @style[:font], false) @textfield >> self end #-------------------------------------------------------------------------- # * Computes self #-------------------------------------------------------------------------- def compute_self super @textfield.width = self.inner.width if @textfield end #-------------------------------------------------------------------------- # * Update #-------------------------------------------------------------------------- def update @textfield.deactivate if Key::Mouse_left.trigger? && !self.hover? @textfield.update end #-------------------------------------------------------------------------- # * position of cursor in screen #-------------------------------------------------------------------------- def cursor_screen_x @textfield.cursor.rect.x + @textfield.inner.abs_x end #-------------------------------------------------------------------------- # * position of cursor in screen #-------------------------------------------------------------------------- def cursor_screen_y @textfield.cursor.rect.y + @textfield.inner.abs_y end #-------------------------------------------------------------------------- # * Mouse trigger #-------------------------------------------------------------------------- def on_mouse_trigger @textfield.activate @textfield.locate end #-------------------------------------------------------------------------- # * set value #-------------------------------------------------------------------------- def value=(d) recorder.value = d @textfield.refresh recorder.cursor_jump(@textfield.formatted_value.length) end end end #============================================================================== # ** Module #------------------------------------------------------------------------------ # Link CSS #============================================================================== class Module include Gui::CSS extend Gui::CSS end #============================================================================== # ** CSS #------------------------------------------------------------------------------ # Telling what the things looks like #============================================================================== module CSS fon = Font.new(Font.default_name, 16) fon.color = Color.new('#FFFFFF') fon.bold = true fon.outline = false #-------------------------------------------------------------------------- # * TextField #-------------------------------------------------------------------------- set 'Gui::TextField', width: 100.percent, padding: 3 #-------------------------------------------------------------------------- # * Pannel #-------------------------------------------------------------------------- set 'Gui::Pannel', title: "title", width: 100.percent, height: 100.percent, border: [20, 2, 2, 2], border_color: Color.new('#113F59') set 'Gui::Label.pannel_title', font: fon, x: 5, y: 2 #-------------------------------------------------------------------------- # * Button #-------------------------------------------------------------------------- set 'Gui::Button', background_color: Color.new('#D54F58'), title: "title", padding: [3, 6], border: 0, font: fon, border: 0 #-------------------------------------------------------------------------- # * CheckBox #-------------------------------------------------------------------------- set 'Gui::CheckBox', padding: [0,4] #-------------------------------------------------------------------------- # * TrackBar & ScrollBar #-------------------------------------------------------------------------- set 'Gui::TrackBar','Gui::VerticalTrackBar', 'Gui::ScrollBar','Gui::VerticalScrollBar', background: :none, padding: 0, border: 0 set '.track', background_color: get_color('gray'), padding: 0 set '.bar', background_color: get_color('white'), border_color: get_color('gray') #-------------------------------------------------------------------------- # * Horizontal TrackBar #-------------------------------------------------------------------------- set 'Gui::TrackBar','Gui::VerticalTrackBar', width: 100.percent, height: 12 set 'Gui::TrackBar .track', width: 100.percent, height: 50.percent, y: 25.percent set 'Gui::TrackBar .bar', width: 8, height: 100.percent #-------------------------------------------------------------------------- # * Vertical TrackBar #-------------------------------------------------------------------------- set 'Gui::VerticalTrackBar', height: 100.percent, width: 12 set 'Gui::VerticalTrackBar .track', width: 50.percent, height: 100.percent, x: 25.percent set 'Gui::VerticalTrackBar .bar', width: 100.percent, height: 8 #-------------------------------------------------------------------------- # * Horizontal Scrollbar #-------------------------------------------------------------------------- set 'Gui::ScrollBar', width: 100.percent, height: 8 set 'Gui::ScrollBar .track', width: 100.percent, height: 100.percent set 'Gui::ScrollBar .bar', width: 20, height: 100.percent #-------------------------------------------------------------------------- # * Vertical Scrollbar #-------------------------------------------------------------------------- set 'Gui::VerticalScrollBar', width: 8, height: 100.percent set 'Gui::VerticalScrollBar .track', width: 100.percent, height: 100.percent set 'Gui::VerticalScrollBar .bar', width: 100.percent, height: 20 end end
x
Éditer le texte

Merci d'entrer le mot de passe que vous avez indiqué à la création du texte.

x
Télécharger le texte

Merci de choisir le format du fichier à télécharger.