Class: Shoes::App
- Includes:
- Log
- Defined in:
- lacci/lib/shoes/app.rb,
lacci/lib/shoes/app.rb
Overview
These methods will need to be defined on Slots too, but probably need a rework in general.
Constant Summary collapse
- CUSTOM_EVENT_LOOP_TYPES =
["displaylib", "return", "wait"]
Constants included from Log
Log::DEFAULT_COMPONENT, Log::DEFAULT_DEBUG_LOG_CONFIG, Log::DEFAULT_LOG_CONFIG
Constants inherited from Drawable
Class Attribute Summary collapse
-
.instance ⇒ Object
Returns the value of attribute instance.
Instance Attribute Summary collapse
-
#dir ⇒ Object
readonly
The application directory for this app.
-
#document_root ⇒ Object
readonly
The Shoes root of the drawable tree.
Attributes inherited from Drawable
#debug_id, #destroyed, #parent
Attributes inherited from Linkable
Instance Method Summary collapse
- #all_drawables ⇒ Object
-
#background ⇒ Object
This is going to go away.
-
#border ⇒ Object
This is going to go away.
-
#current_draw_context ⇒ Hash
Get the current draw context for the current slot.
- #current_slot ⇒ Object
- #destroy(send_event: true) ⇒ Object
-
#features ⇒ Object
This is defined to avoid the linkable-id check in the Shoes-style method_missing def'n.
-
#find_drawables_by(*specs) ⇒ Object
We can add various ways to find drawables here.
- #init ⇒ Object
-
#initialize(title: "Shoes!", width: 480, height: 420, resizable: true, features: [], &app_code_body) ⇒ App
constructor
A new instance of App.
- #line_to(x, y) ⇒ Object
-
#method_missing(name, *args, **kwargs, &block) ⇒ Object
We use method_missing for drawable-creating methods like "button".
-
#move_to(x, y) ⇒ Object
Shape DSL methods.
- #pop_slot ⇒ Object
-
#push_slot(slot) ⇒ Object
"Container" drawables like flows, stacks, masks and the document root are considered "slots" in Shoes parlance.
-
#run ⇒ Object
This usually doesn't return.
- #with_slot(slot_item, &block) ⇒ Object
Methods included from Log
configure_logger, #log_init, logger
Methods inherited from Drawable
allocate_drawable_id, #app, #banner, #caption, convert_to_float, convert_to_integer, #download, drawable_by_id, drawable_class_by_name, dsl_name, #event, expects_parent?, feature_for_shoes_style, get_shoes_events, #hide, #hover, init_args, #inscription, #inspect, is_widget_class?, #leave, #motion, opt_init_args, optional_init_args, register_drawable_id, registered_shoes_events?, required_init_args, #respond_to_missing?, #set_parent, shoes_events, shoes_style, shoes_style_hashes, shoes_style_name?, shoes_style_names, #shoes_style_values, shoes_styles, #show, #style, #subtitle, #tagline, #title, #toggle, unregister_drawable_id, validate_as
Methods included from MarginHelper
Methods included from Colors
Methods inherited from Linkable
#bind_shoes_event, #send_self_event, #send_shoes_event, #unsub_all_shoes_events, #unsub_shoes_event
Constructor Details
#initialize(title: "Shoes!", width: 480, height: 420, resizable: true, features: [], &app_code_body) ⇒ App
Returns a new instance of App.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lacci/lib/shoes/app.rb', line 28 def initialize( title: "Shoes!", width: 480, height: 420, resizable: true, features: [], &app_code_body ) log_init("Shoes::App") if Shoes::App.instance @log.error("Trying to create a second Shoes::App in the same process! Fail!") raise Shoes::Errors::TooManyInstancesError, "Cannot create multiple Shoes::App objects!" else Shoes::App.instance = self end # We cd to the app's containing dir when running the app @dir = Dir.pwd @do_shutdown = false @event_loop_type = "displaylib" # the default @features = features unknown_ext = features - Shoes::FEATURES - Shoes::EXTENSIONS unsupported_features = unknown_ext & Shoes::KNOWN_FEATURES unless unsupported_features.empty? @log.error("Shoes app requires feature(s) not supported by this display service: #{unsupported_features.inspect}!") raise Shoes::Errors::UnsupportedFeatureError, "Shoes app needs features: #{unsupported_features.inspect}" end unless unknown_ext.empty? @log.warn("Shoes app requested unknown features #{unknown_ext.inspect}! Known: #{(Shoes::FEATURES + Shoes::EXTENSIONS).inspect}") end @slots = [] super # This creates the DocumentRoot, including its corresponding display drawable @document_root = Shoes::DocumentRoot.new # Now create the App display drawable create_display_drawable # Set up testing *after* Display Service basic objects exist if ENV["SHOES_SPEC_TEST"] test_code = File.read ENV["SHOES_SPEC_TEST"] unless test_code.empty? Shoes::Spec.instance.run_shoes_spec_test_code test_code end end @app_code_body = app_code_body # Try to de-dup as much as possible and not send repeat or multiple # destroy events @watch_for_destroy = bind_shoes_event(event_name: "destroy") do Shoes::DisplayService.unsub_from_events(@watch_for_destroy) if @watch_for_destroy @watch_for_destroy = nil self.destroy(send_event: false) end @watch_for_event_loop = bind_shoes_event(event_name: "custom_event_loop") do |loop_type| raise(Shoes::Errors::InvalidAttributeValueError, "Unknown event loop type: #{loop_type.inspect}!") unless CUSTOM_EVENT_LOOP_TYPES.include?(loop_type) @event_loop_type = loop_type end Signal.trap("INT") do @log.warn("App interrupted by signal, stopping...") puts "\nStopping Shoes app..." destroy end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, **kwargs, &block) ⇒ Object
We use method_missing for drawable-creating methods like "button". The parent's method_missing will auto-create Shoes style getters and setters. This is similar to the method_missing in Shoes::Slot, but different in where the new drawable appears.
142 143 144 145 146 147 148 149 150 151 |
# File 'lacci/lib/shoes/app.rb', line 142 def method_missing(name, *args, **kwargs, &block) klass = ::Shoes::Drawable.drawable_class_by_name(name) return super unless klass ::Shoes::App.define_method(name) do |*args, **kwargs, &block| klass.new(*args, **kwargs, &block) end send(name, *args, **kwargs, &block) end |
Class Attribute Details
.instance ⇒ Object
Returns the value of attribute instance.
8 9 10 |
# File 'lacci/lib/shoes/app.rb', line 8 def instance @instance end |
Instance Attribute Details
#dir ⇒ Object (readonly)
The application directory for this app. Often this will be the directory containing the launched application file.
16 17 18 |
# File 'lacci/lib/shoes/app.rb', line 16 def dir @dir end |
#document_root ⇒ Object (readonly)
The Shoes root of the drawable tree
12 13 14 |
# File 'lacci/lib/shoes/app.rb', line 12 def document_root @document_root end |
Instance Method Details
#all_drawables ⇒ Object
196 197 198 199 200 201 202 203 204 205 206 |
# File 'lacci/lib/shoes/app.rb', line 196 def all_drawables out = [] to_add = [@document_root, @document_root.children] until to_add.empty? out.concat(to_add) to_add = to_add.flat_map { |w| w.respond_to?(:children) ? w.children : [] }.compact end out end |
#background ⇒ Object
This is going to go away. See issue #496
266 267 268 |
# File 'lacci/lib/shoes/app.rb', line 266 def background(...) current_slot.background(...) end |
#border ⇒ Object
This is going to go away. See issue #498
271 272 273 |
# File 'lacci/lib/shoes/app.rb', line 271 def border(...) current_slot.border(...) end |
#current_draw_context ⇒ Hash
Get the current draw context for the current slot
156 157 158 |
# File 'lacci/lib/shoes/app.rb', line 156 def current_draw_context current_slot&.current_draw_context end |
#current_slot ⇒ Object
125 126 127 |
# File 'lacci/lib/shoes/app.rb', line 125 def current_slot @slots[-1] end |
#destroy(send_event: true) ⇒ Object
191 192 193 194 |
# File 'lacci/lib/shoes/app.rb', line 191 def destroy(send_event: true) @do_shutdown = true send_shoes_event(event_name: "destroy") if send_event end |
#features ⇒ Object
This is defined to avoid the linkable-id check in the Shoes-style method_missing def'n
21 22 23 |
# File 'lacci/lib/shoes/app.rb', line 21 def features @features end |
#find_drawables_by(*specs) ⇒ Object
We can add various ways to find drawables here. These are sort of like Shoes selectors, used for testing.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lacci/lib/shoes/app.rb', line 210 def find_drawables_by(*specs) drawables = all_drawables specs.each do |spec| if spec == Shoes::App drawables = [Shoes::App.instance] elsif spec.is_a?(Class) drawables.select! { |w| spec === w } elsif spec.is_a?(Symbol) || spec.is_a?(String) s = spec.to_s case s[0] when "$" begin # I'm not finding a global_variable_get or similar... global_value = eval s drawables &= [global_value] rescue raise Shoes::Errors::InvalidAttributeValueError, "Error getting global variable: #{spec.inspect}" end when "@" if Shoes::App.instance.instance_variables.include?(spec.to_sym) drawables &= [self.instance_variable_get(spec)] else raise Shoes::Errors::InvalidAttributeValueError, "Can't find top-level instance variable: #{spec.inspect}!" end else if s.start_with?("id:") find_id = Integer(s[3..-1]) drawable = Shoes::Drawable.drawable_by_id(find_id) drawables &= [drawable] else raise Shoes::Errors::InvalidAttributeValueError, "Don't know how to find drawables by #{spec.inspect}!" end end else raise(Shoes::Errors::InvalidAttributeValueError, "Don't know how to find drawables by #{spec.inspect}!") end end drawables end |
#init ⇒ Object
105 106 107 108 109 110 |
# File 'lacci/lib/shoes/app.rb', line 105 def init send_shoes_event(event_name: "init") return if @do_shutdown ::Shoes::App.instance.with_slot(@document_root, &@app_code_body) end |
#line_to(x, y) ⇒ Object
292 293 294 295 296 297 298 |
# File 'lacci/lib/shoes/app.rb', line 292 def line_to(x, y) raise(Shoes::Errors::InvalidAttributeValueError, "Pass only Numeric arguments to line_to!") unless x.is_a?(Numeric) && y.is_a?(Numeric) if current_slot.is_a?(::Shoes::Shape) current_slot.add_shape_command(["line_to", x, y]) end end |
#move_to(x, y) ⇒ Object
Shape DSL methods
284 285 286 287 288 289 290 |
# File 'lacci/lib/shoes/app.rb', line 284 def move_to(x, y) raise(Shoes::Errors::InvalidAttributeValueError, "Pass only Numeric arguments to move_to!") unless x.is_a?(Numeric) && y.is_a?(Numeric) if current_slot.is_a?(::Shoes::Shape) current_slot.add_shape_command(["move_to", x, y]) end end |
#pop_slot ⇒ Object
119 120 121 122 123 |
# File 'lacci/lib/shoes/app.rb', line 119 def pop_slot return if @slots.size <= 1 @slots.pop end |
#push_slot(slot) ⇒ Object
"Container" drawables like flows, stacks, masks and the document root are considered "slots" in Shoes parlance. When a new slot is created, we push it here in order to track what drawables are found in that slot.
115 116 117 |
# File 'lacci/lib/shoes/app.rb', line 115 def push_slot(slot) @slots.push(slot) end |
#run ⇒ Object
This usually doesn't return. The display service may take control of the main thread. Local Webview even stops any background threads. However, some display libraries don't want to shut down and don't want to (and/or can't) take control of the event loop.
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lacci/lib/shoes/app.rb', line 164 def run if @do_shutdown $stderr.puts "Destroy has already been signaled, but we just called Shoes::App.run!" return end # The display lib can send us an event to customise the event loop handling. # But it must do so before the "run" event returns. send_shoes_event(event_name: "run") case @event_loop_type when "wait" # Display lib wants us to busy-wait instead of it. until @do_shutdown Shoes::DisplayService.dispatch_event("heartbeat", nil) end when "displaylib" # If run event returned, that means we're done. destroy when "return" # We can just return to the main event loop. But we shouldn't call destroy. # Presumably some event loop *outside* our event loop is handling things. else raise Shoes::Errors::InvalidAttributeValueError, "Internal error! Incorrect event loop type: #{@event_loop_type.inspect}!" end end |