fidgit 0.0.2alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/COPYING.txt +674 -0
- data/Gemfile +4 -0
- data/README.textile +138 -0
- data/Rakefile +38 -0
- data/config/default_schema.yml +180 -0
- data/examples/_all_examples.rb +9 -0
- data/examples/align_example.rb +56 -0
- data/examples/button_and_toggle_button_example.rb +27 -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 +17 -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 +30 -0
- data/examples/splash_example.rb +42 -0
- data/examples/text_area_example.rb +28 -0
- data/fidgit.gemspec +28 -0
- data/lib/fidgit.rb +4 -0
- data/lib/fidgit/chingu_ext/window.rb +6 -0
- data/lib/fidgit/clipboard.rb +23 -0
- data/lib/fidgit/cursor.rb +38 -0
- data/lib/fidgit/elements/button.rb +68 -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 +85 -0
- data/lib/fidgit/elements/composite.rb +17 -0
- data/lib/fidgit/elements/container.rb +187 -0
- data/lib/fidgit/elements/element.rb +252 -0
- data/lib/fidgit/elements/file_browser.rb +152 -0
- data/lib/fidgit/elements/grid_packer.rb +219 -0
- data/lib/fidgit/elements/group.rb +66 -0
- data/lib/fidgit/elements/horizontal_packer.rb +12 -0
- data/lib/fidgit/elements/label.rb +77 -0
- data/lib/fidgit/elements/list.rb +47 -0
- data/lib/fidgit/elements/menu_pane.rb +149 -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 +75 -0
- data/lib/fidgit/elements/scroll_bar.rb +114 -0
- data/lib/fidgit/elements/scroll_window.rb +92 -0
- data/lib/fidgit/elements/slider.rb +119 -0
- data/lib/fidgit/elements/text_area.rb +351 -0
- data/lib/fidgit/elements/toggle_button.rb +67 -0
- data/lib/fidgit/elements/tool_tip.rb +35 -0
- data/lib/fidgit/elements/vertical_packer.rb +12 -0
- data/lib/fidgit/event.rb +99 -0
- data/lib/fidgit/gosu_ext/color.rb +123 -0
- data/lib/fidgit/history.rb +85 -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 +42 -0
- data/lib/fidgit/states/file_dialog.rb +24 -0
- data/lib/fidgit/states/gui_state.rb +301 -0
- data/lib/fidgit/states/message_dialog.rb +61 -0
- data/lib/fidgit/thumbnail.rb +29 -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/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/label_spec.rb +49 -0
- data/spec/fidgit/event_spec.rb +149 -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/helpers/tex_play_helper.rb +9 -0
- data/spec/fidgit/history_spec.rb +144 -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
- data/spec/fidgit/thumbnail_spec.rb +50 -0
- metadata +177 -0
@@ -0,0 +1,187 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
# A container that contains Elements.
|
5
|
+
# @abstract
|
6
|
+
class Container < Element
|
7
|
+
def size; @children.size; end
|
8
|
+
def each(&block); @children.each █ end
|
9
|
+
def find(&block); @children.find █ end
|
10
|
+
def index(value); @children.index value; end
|
11
|
+
def [](index); @children[index]; end
|
12
|
+
|
13
|
+
def x=(value)
|
14
|
+
each {|c| c.x += value - x }
|
15
|
+
super(value)
|
16
|
+
end
|
17
|
+
|
18
|
+
def y=(value)
|
19
|
+
each {|c| c.y += value - y }
|
20
|
+
super(value)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param (see Element#initialize)
|
24
|
+
#
|
25
|
+
# @option (see Element#initialize)
|
26
|
+
def initialize(options = {})
|
27
|
+
options[:border_color] = default(:debug, :border_color) if Fidgit.debug_mode?
|
28
|
+
|
29
|
+
@children = []
|
30
|
+
|
31
|
+
super(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def add(element)
|
35
|
+
element.send :parent=, self
|
36
|
+
@children.push element
|
37
|
+
|
38
|
+
recalc
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove(element)
|
43
|
+
@children.delete element
|
44
|
+
element.send :parent=, nil
|
45
|
+
|
46
|
+
recalc
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def insert(position, element)
|
51
|
+
@children.insert position, element
|
52
|
+
element.send :parent=, self
|
53
|
+
|
54
|
+
recalc
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Create a button within the container.
|
59
|
+
def button(text, options = {}, &block)
|
60
|
+
Button.new(text, {parent: self}.merge!(options), &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Create a color picker within the container.
|
64
|
+
def color_picker(options = {}, &block)
|
65
|
+
ColorPicker.new({parent: self}.merge!(options), &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Create a color well within the container.
|
69
|
+
def color_well(options = {}, &block)
|
70
|
+
ColorWell.new({parent: self}.merge!(options), &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def combo_box(options = {}, &block)
|
74
|
+
ComboBox.new({parent: self}.merge!(options), &block)
|
75
|
+
end
|
76
|
+
|
77
|
+
def file_browser(type, options = {}, &block)
|
78
|
+
FileBrowser.new(type, {parent: self}.merge!(options), &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
def group(options = {}, &block)
|
82
|
+
Group.new({parent: self}.merge!(options), &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create a label within the container.
|
86
|
+
def label(text, options = {})
|
87
|
+
Label.new(text, {parent: self}.merge!(options))
|
88
|
+
end
|
89
|
+
|
90
|
+
def list(options = {}, &block)
|
91
|
+
List.new({parent: self}.merge!(options), &block)
|
92
|
+
end
|
93
|
+
|
94
|
+
def pack(arrangement, options = {}, &block)
|
95
|
+
klass = case arrangement
|
96
|
+
when :horizontal
|
97
|
+
HorizontalPacker
|
98
|
+
when :vertical
|
99
|
+
VerticalPacker
|
100
|
+
when :grid
|
101
|
+
GridPacker
|
102
|
+
else
|
103
|
+
raise ArgumentError, "packing arrangement must be one of :horizontal, :vertical or :grid"
|
104
|
+
end
|
105
|
+
|
106
|
+
klass.new({parent: self}.merge!(options), &block)
|
107
|
+
end
|
108
|
+
|
109
|
+
def radio_button(text, value, options = {}, &block)
|
110
|
+
RadioButton.new(text, value, {parent: self}.merge!(options), &block)
|
111
|
+
end
|
112
|
+
|
113
|
+
def scroll_area(options = {}, &block)
|
114
|
+
ScrollArea.new({parent: self}.merge!(options), &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
def scroll_window(options = {}, &block)
|
118
|
+
ScrollWindow.new({parent: self}.merge!(options), &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
def slider(options = {}, &block)
|
122
|
+
Slider.new({parent: self}.merge!(options), &block)
|
123
|
+
end
|
124
|
+
|
125
|
+
def text_area(options = {}, &block)
|
126
|
+
TextArea.new({parent: self}.merge!(options), &block)
|
127
|
+
end
|
128
|
+
|
129
|
+
def toggle_button(text, options = {}, &block)
|
130
|
+
ToggleButton.new(text, {parent: self}.merge!(options), &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
def clear
|
134
|
+
@children.each {|child| child.send :parent=, nil }
|
135
|
+
@children.clear
|
136
|
+
|
137
|
+
recalc
|
138
|
+
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
142
|
+
def update
|
143
|
+
each { |c| c.update }
|
144
|
+
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns the element within this container that was hit,
|
149
|
+
# @return [Element, nil] The element hit, otherwise nil.
|
150
|
+
def hit_element(x, y)
|
151
|
+
@children.reverse_each do |child|
|
152
|
+
case child
|
153
|
+
when Container, Composite
|
154
|
+
if element = child.hit_element(x, y)
|
155
|
+
return element
|
156
|
+
end
|
157
|
+
else
|
158
|
+
return child if child.hit?(x, y)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
self if hit?(x, y)
|
163
|
+
end
|
164
|
+
|
165
|
+
protected
|
166
|
+
def draw_foreground
|
167
|
+
each { |c| c.draw }
|
168
|
+
|
169
|
+
font.draw self.class.name, x, y, z if Fidgit.debug_mode?
|
170
|
+
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
|
174
|
+
protected
|
175
|
+
# Any container passed a block will allow you access to its methods.
|
176
|
+
def post_init_block(&block)
|
177
|
+
case block.arity
|
178
|
+
when 1
|
179
|
+
yield self
|
180
|
+
when 0
|
181
|
+
instance_methods_eval &block
|
182
|
+
else
|
183
|
+
raise "block arity must be 0 or 1"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The Fidgit GUI framework for Gosu.
|
4
|
+
module Fidgit
|
5
|
+
class << self
|
6
|
+
attr_accessor :debug_mode
|
7
|
+
end
|
8
|
+
|
9
|
+
self.debug_mode = false
|
10
|
+
|
11
|
+
def self.debug_mode?; debug_mode; end
|
12
|
+
|
13
|
+
# An element within the GUI environment.
|
14
|
+
# @abstract
|
15
|
+
class Element
|
16
|
+
include Event
|
17
|
+
|
18
|
+
event :left_mouse_button
|
19
|
+
event :holding_left_mouse_button
|
20
|
+
event :released_left_mouse_button
|
21
|
+
event :clicked_left_mouse_button
|
22
|
+
|
23
|
+
event :right_mouse_button
|
24
|
+
event :holding_right_mouse_button
|
25
|
+
event :released_right_mouse_button
|
26
|
+
event :clicked_right_mouse_button
|
27
|
+
|
28
|
+
event :mouse_wheel_up
|
29
|
+
event :mouse_wheel_down
|
30
|
+
|
31
|
+
event :enter
|
32
|
+
event :hover
|
33
|
+
event :leave
|
34
|
+
|
35
|
+
DEFAULT_SCHEMA_FILE = File.expand_path(File.join(__FILE__, '..', '..', '..', '..', 'config', 'default_schema.yml'))
|
36
|
+
|
37
|
+
VALID_ALIGN_H = [:left, :center, :right, :fill]
|
38
|
+
VALID_ALIGN_V = [:top, :center, :bottom, :fill]
|
39
|
+
|
40
|
+
attr_reader :z, :tip, :font_size, :padding_top, :padding_right, :padding_bottom, :padding_left, :align_h, :align_v, :parent, :border_thickness
|
41
|
+
|
42
|
+
attr_accessor :background_color
|
43
|
+
|
44
|
+
def x; rect.x; end
|
45
|
+
def x=(value); rect.x = value; end
|
46
|
+
|
47
|
+
def y; rect.y; end
|
48
|
+
def y=(value); rect.y = value; end
|
49
|
+
|
50
|
+
# Width not including border.
|
51
|
+
def width; rect.width; end
|
52
|
+
def width=(value); rect.width = [[value, @width_range.max].min, @width_range.min].max; end
|
53
|
+
def min_width; @width_range.min; end
|
54
|
+
def max_width; @width_range.max; end
|
55
|
+
# Width including border thickness.
|
56
|
+
def outer_width; rect.width + @border_thickness * 2; end
|
57
|
+
|
58
|
+
# Height not including border.
|
59
|
+
def height; rect.height; end
|
60
|
+
def height=(value); rect.height = [[value, @height_range.max].min, @height_range.min].max; end
|
61
|
+
def min_height; @height_range.min; end
|
62
|
+
def max_height; @height_range.max; end
|
63
|
+
# Height including border thickness.
|
64
|
+
def outer_height; rect.height + @border_thickness * 2; end
|
65
|
+
|
66
|
+
# Can the object be dragged?
|
67
|
+
def drag?(button); false; end
|
68
|
+
|
69
|
+
def enabled?; @enabled; end
|
70
|
+
def enabled=(value); @enabled = value; end
|
71
|
+
|
72
|
+
def font; @font ||= Gosu::Font[@font_name, @font_size]; end
|
73
|
+
|
74
|
+
def rect; @rect; end; protected :rect
|
75
|
+
|
76
|
+
def self.schema; @@schema ||= Schema.new(YAML.load(File.read(DEFAULT_SCHEMA_FILE)));; end
|
77
|
+
|
78
|
+
class << self
|
79
|
+
alias_method :original_new, :new
|
80
|
+
|
81
|
+
def new(*args, &block)
|
82
|
+
obj = original_new(*args) # Block should be ignored.
|
83
|
+
obj.send :post_init
|
84
|
+
obj.send :post_init_block, &block if block_given?
|
85
|
+
obj
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get the default value from the schema.
|
90
|
+
#
|
91
|
+
# @param [Symbol, Array<Symbol>] names
|
92
|
+
def default(*names)
|
93
|
+
self.class.schema.default(self.class, names)
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param [Element, nil] parent
|
97
|
+
#
|
98
|
+
# @option options [Number] :x (0)
|
99
|
+
# @option options [Number] :y (0)
|
100
|
+
# @option options [Number] :z (0)
|
101
|
+
#
|
102
|
+
# @option options [Number] :width (auto)
|
103
|
+
# @option options [Number] :min_width (value of :width option)
|
104
|
+
# @option options [Number] :max_width (value of :width option)
|
105
|
+
#
|
106
|
+
# @option options [Number] :height (auto)
|
107
|
+
# @option options [Number] :min_height (value of :height option)
|
108
|
+
# @option options [Number] :max_height (value of :height option)
|
109
|
+
#
|
110
|
+
# @option options [String] :tip ('') Tool-tip text
|
111
|
+
# @option options [String] :font_name ('')
|
112
|
+
# @option options [String] :font_size (30)
|
113
|
+
#
|
114
|
+
# @option options [Gosu::Color] :background_color (transparent)
|
115
|
+
# @option options [Gosu::Color] :border_color (transparent)
|
116
|
+
#
|
117
|
+
# @option options [Boolean] :enabled (true)
|
118
|
+
#
|
119
|
+
# @option options [Number] :padding (4)
|
120
|
+
# @option options [Number] :padding_h (:padding option)
|
121
|
+
# @option options [Number] :padding_v (:padding option)
|
122
|
+
# @option options [Number] :padding_top (:padding_v option)
|
123
|
+
# @option options [Number] :padding_right (:padding_h option)
|
124
|
+
# @option options [Number] :padding_bottom (:padding_v option)
|
125
|
+
# @option options [Number] :padding_left (:padding_h option)
|
126
|
+
#
|
127
|
+
# @option options [Symbol] :align Align both horizontally and vertically. One of :center, :fill or [<align_v>, <align_h>] such as [:top, :right].
|
128
|
+
# @option options [Symbol] :align_h (value or :align else :left) One of :left, :center, :right :fill
|
129
|
+
# @option options [Symbol] :align_v (value of :align else :top) One of :top, :center, :bottom, :fill
|
130
|
+
|
131
|
+
# @yield instance_methods_eval with respect to self.
|
132
|
+
def initialize(options = {}, &block)
|
133
|
+
options = {
|
134
|
+
x: 0,
|
135
|
+
y: 0,
|
136
|
+
z: 0,
|
137
|
+
tip: '',
|
138
|
+
font_name: default(:font_name),
|
139
|
+
font_size: default(:font_size),
|
140
|
+
background_color: default(:background_color),
|
141
|
+
border_color: default(:border_color),
|
142
|
+
border_thickness: default(:border_thickness),
|
143
|
+
enabled: true,
|
144
|
+
}.merge! options
|
145
|
+
|
146
|
+
@enabled = options[:enabled]
|
147
|
+
|
148
|
+
# Alignment and min/max dimensions.
|
149
|
+
@align_h = options[:align_h] || Array(options[:align]).last || default(:align_h)
|
150
|
+
raise ArgumentError, "Invalid align_h: #{@align_h}" unless VALID_ALIGN_H.include? @align_h
|
151
|
+
|
152
|
+
min_width = (options[:min_width] || options[:width] || 0)
|
153
|
+
max_width = (options[:max_width] || options[:width] || Float::INFINITY)
|
154
|
+
@width_range = min_width..max_width
|
155
|
+
|
156
|
+
@align_v = options[:align_v] || Array(options[:align]).first || default(:align_v)
|
157
|
+
raise ArgumentError, "Invalid align_v: #{@align_v}" unless VALID_ALIGN_V.include? @align_v
|
158
|
+
|
159
|
+
min_height = (options[:min_height] || options[:height] || 0)
|
160
|
+
max_height = (options[:max_height] || options[:height] || Float::INFINITY)
|
161
|
+
@height_range = min_height..max_height
|
162
|
+
|
163
|
+
@background_color = options[:background_color].dup
|
164
|
+
@border_color = options[:border_color].dup
|
165
|
+
@border_thickness = options[:border_thickness]
|
166
|
+
|
167
|
+
@padding_top = options[:padding_top] || options[:padding_v] || options[:padding] || default(:padding_top)
|
168
|
+
@padding_right = options[:padding_right] || options[:padding_h] || options[:padding] || default(:padding_right)
|
169
|
+
@padding_bottom = options[:padding_bottom] || options[:padding_v] || options[:padding] || default(:padding_bottom)
|
170
|
+
@padding_left = options[:padding_left] || options[:padding_h] || options[:padding] || default(:padding_left)
|
171
|
+
self.parent = options[:parent]
|
172
|
+
|
173
|
+
@z = options[:z]
|
174
|
+
@tip = options[:tip].dup
|
175
|
+
@font_name = options[:font_name].dup
|
176
|
+
@font_size = options[:font_size]
|
177
|
+
|
178
|
+
@rect = Chingu::Rect.new(options[:x], options[:y], options[:width] || 0, options[:height] || 0)
|
179
|
+
end
|
180
|
+
|
181
|
+
def recalc
|
182
|
+
old_width, old_height = width, height
|
183
|
+
layout
|
184
|
+
parent.recalc if parent and (width != old_width or height != old_height)
|
185
|
+
|
186
|
+
nil
|
187
|
+
end
|
188
|
+
|
189
|
+
# Check if a point (screen coordinates) is over the element.
|
190
|
+
def hit?(x, y)
|
191
|
+
@rect.collide_point?(x, y)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Redraw the element.
|
195
|
+
def draw
|
196
|
+
draw_background
|
197
|
+
draw_border
|
198
|
+
draw_foreground
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
|
202
|
+
# Update the element.
|
203
|
+
def update
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def draw_rect(*args)
|
208
|
+
$window.current_game_state.draw_rect(*args)
|
209
|
+
end
|
210
|
+
|
211
|
+
def draw_frame(*args)
|
212
|
+
$window.current_game_state.draw_frame(*args)
|
213
|
+
end
|
214
|
+
|
215
|
+
protected
|
216
|
+
def parent=(parent); @parent = parent; end
|
217
|
+
|
218
|
+
protected
|
219
|
+
def draw_background
|
220
|
+
draw_rect(x, y, width, height, z, @background_color) unless @background_color.transparent?
|
221
|
+
end
|
222
|
+
|
223
|
+
protected
|
224
|
+
def draw_border
|
225
|
+
draw_frame(x, y, width, height, @border_thickness, z, @border_color) if @border_thickness > 0 and not @border_color.transparent?
|
226
|
+
end
|
227
|
+
|
228
|
+
protected
|
229
|
+
def draw_foreground
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
|
233
|
+
protected
|
234
|
+
# Should be overridden in children to recalculate the width and height of the element and, if a container
|
235
|
+
# manage the positions of its children.
|
236
|
+
def layout
|
237
|
+
nil
|
238
|
+
end
|
239
|
+
|
240
|
+
protected
|
241
|
+
def post_init
|
242
|
+
recalc
|
243
|
+
@parent.send :add, self if @parent
|
244
|
+
end
|
245
|
+
|
246
|
+
protected
|
247
|
+
# By default, elements do not accept block arguments.
|
248
|
+
def post_init_block(&block)
|
249
|
+
raise ArgumentError, "does not accept a block"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Fidgit
|
4
|
+
class FileBrowser < Composite
|
5
|
+
VALID_TYPES = [:open, :save]
|
6
|
+
|
7
|
+
event :selected
|
8
|
+
|
9
|
+
attr_reader :pattern, :base_directory
|
10
|
+
|
11
|
+
def show_extension?; @show_extension; end
|
12
|
+
def directory
|
13
|
+
dir = File.join(*@directories)
|
14
|
+
dir = File.join(@base_directory, dir) unless @base_directory.empty?
|
15
|
+
dir
|
16
|
+
end
|
17
|
+
def file_name; @file_name_text.text; end
|
18
|
+
def file_path; File.join(directory, file_name); end
|
19
|
+
|
20
|
+
# @param [Symbol] type One of :open, :save
|
21
|
+
# @option options [String] :base_directory ('') Outermost directory that the browser will see.
|
22
|
+
# @option options [String] :directory (current working directory).
|
23
|
+
# @option options [String] :file_name ('') Initially selected file in the directory.
|
24
|
+
# @option options [String] :pattern ('*.*')
|
25
|
+
# @option options [Boolean] :show_extension (true)
|
26
|
+
def initialize(type, options = {})
|
27
|
+
options = {
|
28
|
+
base_directory: '',
|
29
|
+
directory: Dir.pwd,
|
30
|
+
file_name: '',
|
31
|
+
pattern: default(:pattern),
|
32
|
+
show_extension: default(:show_extension),
|
33
|
+
width: 400,
|
34
|
+
save_text: "Save",
|
35
|
+
open_text: "Open",
|
36
|
+
cancel_text: "Cancel",
|
37
|
+
}.merge! options
|
38
|
+
|
39
|
+
@type = type
|
40
|
+
raise ArgumentError, "type must be one of #{VALID_TYPES}, not #{@type}" unless VALID_TYPES.include? @type
|
41
|
+
|
42
|
+
@pattern = options[:pattern]
|
43
|
+
@show_extension = options[:show_extension]
|
44
|
+
@base_directory = options[:base_directory].chomp File::SEPARATOR
|
45
|
+
|
46
|
+
@directories = options[:directory].sub(/^#{@base_directory}/, '').split(File::SEPARATOR)
|
47
|
+
if @directories.first == ''
|
48
|
+
@directories[0] = File::SEPARATOR
|
49
|
+
end
|
50
|
+
|
51
|
+
super options
|
52
|
+
|
53
|
+
pack :vertical do
|
54
|
+
@nav_buttons = pack :horizontal, padding: 0, spacing: 2
|
55
|
+
|
56
|
+
@scroll_window = scroll_window(height: 250, width: options[:width]) do
|
57
|
+
@files_list = list(width: options[:width]) do
|
58
|
+
subscribe :changed do |sender, file_path|
|
59
|
+
if file_path
|
60
|
+
file_name = File.basename file_path
|
61
|
+
if File.directory? file_path
|
62
|
+
@directories.push file_name
|
63
|
+
create_nav_buttons
|
64
|
+
update_files_list
|
65
|
+
else
|
66
|
+
@file_name_text.text = file_name
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
@file_name_text = text_area(text: options[:file_name], max_height: font_size * 1.5, width: options[:width], border_thickness: 1)
|
74
|
+
|
75
|
+
create_nav_buttons
|
76
|
+
|
77
|
+
pack :horizontal, align: :center, padding: 0 do
|
78
|
+
@action_button = button(options[:"#{type}_text"]) do
|
79
|
+
publish :selected, @type, file_path
|
80
|
+
end
|
81
|
+
|
82
|
+
button(options[:cancel_text]) do
|
83
|
+
publish :selected, :cancel, file_path
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Ensure that the open/save button is enabled only when the path is sensible.
|
88
|
+
@file_name_text.subscribe :changed do |sender, text|
|
89
|
+
@action_button.enabled = case @type
|
90
|
+
when :open
|
91
|
+
File.exists? file_path and not File.directory? file_path
|
92
|
+
when :save
|
93
|
+
not text.empty?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
update_files_list
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
def create_nav_buttons(size = @directories.size)
|
103
|
+
@nav_buttons.clear
|
104
|
+
|
105
|
+
@directories = @directories[0..size]
|
106
|
+
|
107
|
+
@directories.each_with_index do |dir, i|
|
108
|
+
if i < @directories.size - 1
|
109
|
+
@nav_buttons.button(dir) do
|
110
|
+
create_nav_buttons(i)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
@nav_buttons.label dir, border_color: @@schema.default(Button, :border_color), border_thickness: @@schema.default(Button, :border_thickness)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
update_files_list
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
def update_files_list
|
122
|
+
@files_list.clear
|
123
|
+
@file_name_text.text = ''
|
124
|
+
@scroll_window.offset_x = @scroll_window.offset_y = 0
|
125
|
+
|
126
|
+
# Add folders.
|
127
|
+
Dir.glob(File.join(directory, "*")).each do |file_path|
|
128
|
+
if File.directory? file_path
|
129
|
+
@files_list.item File.basename(file_path), file_path, icon: Gosu::Image["file_directory.png"]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Add files that match the pattern.
|
134
|
+
Dir.glob(File.join(directory, pattern)).each do |file_path|
|
135
|
+
unless File.directory? file_path
|
136
|
+
file_name = if @show_extension
|
137
|
+
File.basename(file_path)
|
138
|
+
else
|
139
|
+
File.basename(file_path, File.extname(file_path))
|
140
|
+
end
|
141
|
+
|
142
|
+
@files_list.item file_name, file_path, icon: Gosu::Image["file_file.png"]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
protected
|
148
|
+
def post_init_block(&block)
|
149
|
+
subscribe :selected, &block
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|