colstrom-fidgit 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +154 -0
- data/Rakefile +38 -0
- data/config/default_schema.yml +216 -0
- data/examples/_all_examples.rb +9 -0
- data/examples/align_example.rb +56 -0
- data/examples/button_and_toggle_button_example.rb +38 -0
- data/examples/color_picker_example.rb +17 -0
- data/examples/color_well_example.rb +25 -0
- data/examples/combo_box_example.rb +24 -0
- data/examples/file_dialog_example.rb +42 -0
- data/examples/grid_packer_example.rb +29 -0
- data/examples/helpers/example_window.rb +17 -0
- data/examples/label_example.rb +23 -0
- data/examples/list_example.rb +23 -0
- data/examples/media/images/head_icon.png +0 -0
- data/examples/menu_pane_example.rb +27 -0
- data/examples/message_dialog_example.rb +65 -0
- data/examples/radio_button_example.rb +37 -0
- data/examples/readme_example.rb +32 -0
- data/examples/scroll_window_example.rb +49 -0
- data/examples/slider_example.rb +34 -0
- data/examples/splash_example.rb +42 -0
- data/examples/text_area_example.rb +33 -0
- data/fidgit.gemspec +35 -0
- data/lib/fidgit.rb +51 -0
- data/lib/fidgit/chingu_ext/window.rb +6 -0
- data/lib/fidgit/cursor.rb +38 -0
- data/lib/fidgit/elements/button.rb +113 -0
- data/lib/fidgit/elements/color_picker.rb +63 -0
- data/lib/fidgit/elements/color_well.rb +39 -0
- data/lib/fidgit/elements/combo_box.rb +115 -0
- data/lib/fidgit/elements/composite.rb +17 -0
- data/lib/fidgit/elements/container.rb +210 -0
- data/lib/fidgit/elements/element.rb +298 -0
- data/lib/fidgit/elements/file_browser.rb +152 -0
- data/lib/fidgit/elements/grid.rb +227 -0
- data/lib/fidgit/elements/group.rb +64 -0
- data/lib/fidgit/elements/horizontal.rb +12 -0
- data/lib/fidgit/elements/image_frame.rb +65 -0
- data/lib/fidgit/elements/label.rb +85 -0
- data/lib/fidgit/elements/list.rb +47 -0
- data/lib/fidgit/elements/main_packer.rb +25 -0
- data/lib/fidgit/elements/menu_pane.rb +163 -0
- data/lib/fidgit/elements/packer.rb +42 -0
- data/lib/fidgit/elements/radio_button.rb +86 -0
- data/lib/fidgit/elements/scroll_area.rb +68 -0
- data/lib/fidgit/elements/scroll_bar.rb +128 -0
- data/lib/fidgit/elements/scroll_window.rb +83 -0
- data/lib/fidgit/elements/slider.rb +125 -0
- data/lib/fidgit/elements/text_area.rb +494 -0
- data/lib/fidgit/elements/text_line.rb +92 -0
- data/lib/fidgit/elements/toggle_button.rb +67 -0
- data/lib/fidgit/elements/tool_tip.rb +35 -0
- data/lib/fidgit/elements/vertical.rb +12 -0
- data/lib/fidgit/event.rb +159 -0
- data/lib/fidgit/gosu_ext/color.rb +136 -0
- data/lib/fidgit/gosu_ext/gosu_module.rb +25 -0
- data/lib/fidgit/history.rb +91 -0
- data/lib/fidgit/redirector.rb +83 -0
- data/lib/fidgit/schema.rb +123 -0
- data/lib/fidgit/selection.rb +106 -0
- data/lib/fidgit/standard_ext/hash.rb +21 -0
- data/lib/fidgit/states/dialog_state.rb +52 -0
- data/lib/fidgit/states/file_dialog.rb +24 -0
- data/lib/fidgit/states/gui_state.rb +331 -0
- data/lib/fidgit/states/message_dialog.rb +61 -0
- data/lib/fidgit/version.rb +5 -0
- data/lib/fidgit/window.rb +19 -0
- data/media/images/arrow.png +0 -0
- data/media/images/combo_arrow.png +0 -0
- data/media/images/file_directory.png +0 -0
- data/media/images/file_file.png +0 -0
- data/media/images/pixel.png +0 -0
- data/spec/fidgit/elements/helpers/helper.rb +3 -0
- data/spec/fidgit/elements/helpers/tex_play_helper.rb +9 -0
- data/spec/fidgit/elements/image_frame_spec.rb +69 -0
- data/spec/fidgit/elements/label_spec.rb +37 -0
- data/spec/fidgit/event_spec.rb +210 -0
- data/spec/fidgit/gosu_ext/color_spec.rb +130 -0
- data/spec/fidgit/gosu_ext/helpers/helper.rb +3 -0
- data/spec/fidgit/helpers/helper.rb +4 -0
- data/spec/fidgit/history_spec.rb +153 -0
- data/spec/fidgit/redirector_spec.rb +78 -0
- data/spec/fidgit/schema_spec.rb +67 -0
- data/spec/fidgit/schema_test.yml +32 -0
- metadata +320 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Gosu
|
2
|
+
class << self
|
3
|
+
alias_method :register_entity_fidgit, :register_entity
|
4
|
+
|
5
|
+
protected
|
6
|
+
def init_entities
|
7
|
+
@entities = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
public
|
11
|
+
def register_entity(name, image)
|
12
|
+
name = name.to_sym
|
13
|
+
register_entity_fidgit(name, image)
|
14
|
+
@entities[name] = image
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
public
|
19
|
+
def entity(name)
|
20
|
+
@entities[name.to_sym]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
init_entities
|
25
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
# Manages a history of actions, along with doing, undoing and redoing those actions.
|
5
|
+
class History
|
6
|
+
# Maximum number of actions in the History before Actions are deleted.
|
7
|
+
DEFAULT_MAX_SIZE = 250
|
8
|
+
|
9
|
+
# An action in the History. Inherit actions from this in order to add them to a History.
|
10
|
+
class Action
|
11
|
+
# Perform the action.
|
12
|
+
def do; raise NotImplementedError, "#{self.class} does not have a do method defined"; end
|
13
|
+
|
14
|
+
# Reverse the action.
|
15
|
+
def undo; raise NotImplementedError, "#{self.class} does not have an undo method defined"; end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is there an action that can be undone?
|
19
|
+
def can_undo?; @last_done >= 0; end
|
20
|
+
|
21
|
+
# Is there an action that has been undone that can now be redone?
|
22
|
+
def can_redo?; @last_done < (@actions.size - 1); end
|
23
|
+
|
24
|
+
def initialize(max_size = DEFAULT_MAX_SIZE)
|
25
|
+
@max_size = max_size
|
26
|
+
@actions = []
|
27
|
+
@last_done = -1 # Last command that was performed.
|
28
|
+
end
|
29
|
+
|
30
|
+
# Perform a History::Action, adding it to the history.
|
31
|
+
# If there are currently any actions that have been undone, they will be permanently lost and cannot be redone.
|
32
|
+
#
|
33
|
+
# @param [History::Action] action Action to be performed
|
34
|
+
def do(action)
|
35
|
+
raise ArgumentError, "Parameter, 'action', expected to be a #{Action}, but received: #{action}" unless action.is_a? Action
|
36
|
+
|
37
|
+
# Remove all undone actions when a new one is performed.
|
38
|
+
if can_redo?
|
39
|
+
if @last_done == -1
|
40
|
+
@actions.clear
|
41
|
+
else
|
42
|
+
@actions = @actions[0..@last_done]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# If history is too big, remove the oldest action.
|
47
|
+
if @actions.size >= @max_size
|
48
|
+
@actions.shift
|
49
|
+
end
|
50
|
+
|
51
|
+
@last_done = @actions.size
|
52
|
+
@actions << action
|
53
|
+
action.do
|
54
|
+
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Perform a History::Action, replacing the last action that was performed.
|
59
|
+
#
|
60
|
+
# @param [History::Action] action Action to be performed
|
61
|
+
def replace_last(action)
|
62
|
+
raise ArgumentError, "Parameter, 'action', expected to be a #{Action}, but received: #{action}" unless action.is_a? Action
|
63
|
+
|
64
|
+
@actions[@last_done].undo
|
65
|
+
@actions[@last_done] = action
|
66
|
+
action.do
|
67
|
+
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# Undo the last action that was performed.
|
72
|
+
def undo
|
73
|
+
raise "Can't undo unless there are commands in past" unless can_undo?
|
74
|
+
|
75
|
+
@actions[@last_done].undo
|
76
|
+
@last_done -= 1
|
77
|
+
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
# Redo the last action that was undone.
|
82
|
+
def redo
|
83
|
+
raise "Can't redo if there are no commands in the future" unless can_redo?
|
84
|
+
|
85
|
+
@last_done += 1
|
86
|
+
@actions[@last_done].do
|
87
|
+
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Fidgit
|
2
|
+
# Redirects methods to an object, but does not mask methods and ivars from the calling context.
|
3
|
+
module RedirectorMethods
|
4
|
+
# Evaluate a block accessing methods and ivars from the calling context, but calling public methods
|
5
|
+
# (not ivars or non-public methods) on this object in preference.
|
6
|
+
def instance_methods_eval(&block)
|
7
|
+
raise ArgumentError, "block required" unless block_given?
|
8
|
+
|
9
|
+
context = eval('self', block.binding)
|
10
|
+
|
11
|
+
context.send :push_redirection_target, self
|
12
|
+
|
13
|
+
begin
|
14
|
+
yield context
|
15
|
+
ensure
|
16
|
+
context.send :pop_redirection_target
|
17
|
+
end
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
def push_redirection_target(target)
|
24
|
+
meta_class = class << self; self; end
|
25
|
+
base_methods = Object.public_instance_methods
|
26
|
+
|
27
|
+
# Redirect just the public methods of the target, less those that are on Object.
|
28
|
+
methods_to_redirect = target.public_methods - base_methods
|
29
|
+
|
30
|
+
# Only hide those public/private/protected methods that are being redirected.
|
31
|
+
methods_overridden = []
|
32
|
+
[:public, :protected, :private].each do |access|
|
33
|
+
methods_to_hide = meta_class.send("#{access}_instance_methods", false) & methods_to_redirect
|
34
|
+
methods_to_hide.each do |meth|
|
35
|
+
# Take a reference to the method we are about to override.
|
36
|
+
methods_overridden.push [meth, method(meth), access]
|
37
|
+
meta_class.send :remove_method, meth
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Add a method, to redirect calls to the target.
|
42
|
+
methods_to_redirect.each do |meth|
|
43
|
+
meta_class.send :define_method, meth do |*args, &block|
|
44
|
+
target.send meth, *args, &block
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
redirection_stack.push [target, methods_overridden, methods_to_redirect]
|
49
|
+
|
50
|
+
target
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
def pop_redirection_target
|
55
|
+
meta_class = class << self; self; end
|
56
|
+
|
57
|
+
target, methods_to_recreate, methods_to_remove = redirection_stack.pop
|
58
|
+
|
59
|
+
# Remove the redirection methods
|
60
|
+
methods_to_remove.reverse_each do |meth|
|
61
|
+
meta_class.send :remove_method, meth
|
62
|
+
end
|
63
|
+
|
64
|
+
# Replace with the previous versions of the methods.
|
65
|
+
methods_to_recreate.reverse_each do |meth, reference, access|
|
66
|
+
meta_class.send :define_method, meth, reference
|
67
|
+
meta_class.send access, meth unless access == :public
|
68
|
+
end
|
69
|
+
|
70
|
+
target
|
71
|
+
end
|
72
|
+
|
73
|
+
# Direct access to the redirection stack.
|
74
|
+
private
|
75
|
+
def redirection_stack
|
76
|
+
@_redirection_stack ||= []
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Object
|
82
|
+
include Fidgit::RedirectorMethods
|
83
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
# An object that manages Schema values. Usually loaded from a YAML file.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# schema = Schema.new(YAML.load(file.read('default_schema.yml')))
|
8
|
+
# default_color = schema.default(Element, :disabled, :color)
|
9
|
+
# schema.merge_schema!(YAML.load(file.read('override_schema.yml'))
|
10
|
+
# overridden_color = schema.default(Element, :disabled, :color)
|
11
|
+
class Schema
|
12
|
+
CONSTANT_PREFIX = '?'
|
13
|
+
|
14
|
+
# @param [Hash<Symbol => Hash>] schema data containing
|
15
|
+
def initialize(schema)
|
16
|
+
@constants = {}
|
17
|
+
@elements = {}
|
18
|
+
|
19
|
+
merge_schema! schema
|
20
|
+
end
|
21
|
+
|
22
|
+
# Merge in a hash containing constant values.
|
23
|
+
#
|
24
|
+
# @param [Hash<Symbol => Hash>] constants_hash Containing :colors, :constants and :elements hashes.
|
25
|
+
def merge_schema!(schema)
|
26
|
+
merge_constants!(schema[:constants]) if schema[:constants]
|
27
|
+
merge_elements!(schema[:elements]) if schema[:elements]
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Merge in a hash containing constant values. Arrays will be resolved as colors in RGBA or RGB format.
|
33
|
+
#
|
34
|
+
# @param [Hash<Symbol => Object>] constants_hash
|
35
|
+
def merge_constants!(constants_hash)
|
36
|
+
constants_hash.each_pair do |name, value|
|
37
|
+
@constants[name] = case value
|
38
|
+
when Array
|
39
|
+
case value.size
|
40
|
+
when 3 then Gosu::Color.rgb(*value)
|
41
|
+
when 4 then Gosu::Color.rgba(*value)
|
42
|
+
else
|
43
|
+
raise "Colors must be in 0..255, RGB or RGBA array format"
|
44
|
+
end
|
45
|
+
else
|
46
|
+
value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Merge in a hash containing default values for each element.
|
54
|
+
#
|
55
|
+
# @param [Hash<Symbol => Hash>] elements_hash
|
56
|
+
def merge_elements!(elements_hash)
|
57
|
+
elements_hash.each_pair do |klass_names, data|
|
58
|
+
klass = Fidgit
|
59
|
+
klass_names.to_s.split('::').each do |klass_name|
|
60
|
+
klass = klass.const_get klass_name
|
61
|
+
end
|
62
|
+
|
63
|
+
raise "elements must be names of classes derived from #{Element}" unless klass.ancestors.include? Fidgit::Element
|
64
|
+
@elements[klass] ||= {}
|
65
|
+
@elements[klass].deep_merge! data
|
66
|
+
end
|
67
|
+
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get the constant value associated with +name+.
|
72
|
+
#
|
73
|
+
# @param [Symbol] name
|
74
|
+
# @return [Object]
|
75
|
+
def constant(name)
|
76
|
+
@constants[name]
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param [Class] klass Class to look for defaults for.
|
80
|
+
# @param [Symbol, Array<Symbol>] names Hash names to search for in that class's schema.
|
81
|
+
def default(klass, names)
|
82
|
+
raise ArgumentError, "#{klass} is not a descendent of the #{Element} class" unless klass.ancestors.include? Element
|
83
|
+
value = default_internal(klass, Array(names), true)
|
84
|
+
raise("Failed to find named value #{names.inspect} for class #{klass}") unless value
|
85
|
+
value
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
# @param [Class] klass Class to look for defaults for.
|
90
|
+
# @param [Array<Symbol>] names Hash names to search for in that class's schema.
|
91
|
+
# @param [Boolean] default_to_outer Whether to default to an outer value (used internally)
|
92
|
+
def default_internal(klass, names, default_to_outer)
|
93
|
+
# Find the value by moving through the nested hash via the names.
|
94
|
+
value = @elements[klass]
|
95
|
+
|
96
|
+
names.each do |name|
|
97
|
+
break unless value.is_a? Hash
|
98
|
+
value = value.has_key?(name) ? value[name] : nil
|
99
|
+
end
|
100
|
+
|
101
|
+
# Convert the value to a color/constant if they are symbols.
|
102
|
+
value = if value.is_a? String and value[0] == CONSTANT_PREFIX
|
103
|
+
str = value[1..-1]
|
104
|
+
constant(str.to_sym) || value # If the value isn't a constant, return the string.
|
105
|
+
else
|
106
|
+
value
|
107
|
+
end
|
108
|
+
|
109
|
+
# If we didn't find the value for this class, default to parent class value.
|
110
|
+
if value.nil? and klass != Element and klass.ancestors.include? Element
|
111
|
+
# Check if any ancestors define the fully named value.
|
112
|
+
value = default_internal(klass.superclass, names, false)
|
113
|
+
end
|
114
|
+
|
115
|
+
if value.nil? and default_to_outer and names.size > 1
|
116
|
+
# Check the outer values (e.g. if [:hover, :color] is not defined, try [:color]).
|
117
|
+
value = default_internal(klass, names[1..-1], true)
|
118
|
+
end
|
119
|
+
|
120
|
+
value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
class Selection
|
5
|
+
MIN_DRAG_DISTANCE = 2
|
6
|
+
|
7
|
+
def size; @items.size; end
|
8
|
+
def empty?; @items.empty?; end
|
9
|
+
def [](index); @items[index]; end
|
10
|
+
def each(&block); @items.each(&block); end
|
11
|
+
def to_a; @items.dup; end
|
12
|
+
def include?(object); @items.include? object; end
|
13
|
+
|
14
|
+
# Current being dragged?
|
15
|
+
def dragging?; @dragging; end
|
16
|
+
|
17
|
+
# Actually moved during a dragging operation?
|
18
|
+
def moved?; @moved; end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@items = []
|
22
|
+
@moved = false
|
23
|
+
@dragging = false
|
24
|
+
end
|
25
|
+
|
26
|
+
def add(object)
|
27
|
+
object.selected = true
|
28
|
+
@items.push(object)
|
29
|
+
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove(object)
|
34
|
+
@items.delete(object)
|
35
|
+
object.selected = false
|
36
|
+
object.dragging = false
|
37
|
+
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear
|
42
|
+
end_drag if dragging?
|
43
|
+
@items.each { |o| o.selected = false; o.dragging = false }
|
44
|
+
@items.clear
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def begin_drag(x, y)
|
50
|
+
@initial_x, @initial_y = x, y
|
51
|
+
@last_x, @last_y = x, y
|
52
|
+
@dragging = true
|
53
|
+
@moved = false
|
54
|
+
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def end_drag
|
59
|
+
@items.each do |object|
|
60
|
+
object.x, object.y = object.x.round, object.y.round
|
61
|
+
object.dragging = false
|
62
|
+
end
|
63
|
+
@dragging = false
|
64
|
+
@moved = false
|
65
|
+
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
# Move all dragged object back to original positions.
|
70
|
+
def reset_drag
|
71
|
+
if moved?
|
72
|
+
@items.each do |o|
|
73
|
+
o.x += @initial_x - @last_x
|
74
|
+
o.y += @initial_y - @last_y
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
self.end_drag
|
79
|
+
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def update_drag(x, y)
|
84
|
+
x, y = x.round, y.round
|
85
|
+
|
86
|
+
# If the mouse has been dragged far enough from the initial click position, then 'pick up' the objects and drag.
|
87
|
+
unless moved?
|
88
|
+
if distance(@initial_x, @initial_y, x, y) > MIN_DRAG_DISTANCE
|
89
|
+
@items.each { |o| o.dragging = true }
|
90
|
+
@moved = true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
if moved?
|
95
|
+
@items.each do |o|
|
96
|
+
o.x += x - @last_x
|
97
|
+
o.y += y - @last_y
|
98
|
+
end
|
99
|
+
|
100
|
+
@last_x, @last_y = x, y
|
101
|
+
end
|
102
|
+
|
103
|
+
self
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Hash
|
2
|
+
# Merge not only the hashes, but all nested hashes as well.
|
3
|
+
# Written by Stefan Rusterholz (apeiros) from http://www.ruby-forum.com/topic/142809
|
4
|
+
def deep_merge!(other)
|
5
|
+
merger = lambda do |key, a, b|
|
6
|
+
(a.is_a?(Hash) && b.is_a?(Hash)) ? a.merge!(b, &merger) : b
|
7
|
+
end
|
8
|
+
|
9
|
+
merge!(other, &merger)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Merge not only the hashes, but all nested hashes as well.
|
13
|
+
# Written by Stefan Rusterholz (apeiros) from http://www.ruby-forum.com/topic/142809
|
14
|
+
def deep_merge(other)
|
15
|
+
merger = lambda do |key, a, b|
|
16
|
+
(a.is_a?(Hash) && b.is_a?(Hash)) ? a.merge(b, &merger) : b
|
17
|
+
end
|
18
|
+
|
19
|
+
merge(other, &merger)
|
20
|
+
end
|
21
|
+
end
|