fidgit 0.0.2alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|