Class: Shoes
- Inherits:
-
Object
- Object
- Shoes
- Defined in:
- lacci/lib/shoes.rb,
lacci/lib/shoes.rb,
lacci/lib/shoes/app.rb,
lacci/lib/shoes/log.rb,
lacci/lib/shoes-spec.rb,
lacci/lib/shoes/border.rb,
lacci/lib/shoes/colors.rb,
lacci/lib/shoes/errors.rb,
lacci/lib/shoes/download.rb,
lacci/lib/shoes/drawable.rb,
lacci/lib/shoes/changelog.rb,
lacci/lib/shoes/constants.rb,
lacci/lib/shoes/background.rb,
lacci/lib/shoes/drawables/arc.rb,
lacci/lib/shoes/drawables/flow.rb,
lacci/lib/shoes/drawables/line.rb,
lacci/lib/shoes/drawables/link.rb,
lacci/lib/shoes/drawables/oval.rb,
lacci/lib/shoes/drawables/para.rb,
lacci/lib/shoes/drawables/para.rb,
lacci/lib/shoes/drawables/rect.rb,
lacci/lib/shoes/drawables/star.rb,
lacci/lib/shoes/display_service.rb,
lacci/lib/shoes/drawables/arrow.rb,
lacci/lib/shoes/drawables/check.rb,
lacci/lib/shoes/drawables/image.rb,
lacci/lib/shoes/drawables/radio.rb,
lacci/lib/shoes/drawables/shape.rb,
lacci/lib/shoes/drawables/stack.rb,
lacci/lib/shoes/drawables/video.rb,
lacci/lib/shoes/drawables/border.rb,
lacci/lib/shoes/drawables/button.rb,
lacci/lib/shoes/drawables/edit_box.rb,
lacci/lib/shoes/drawables/list_box.rb,
lacci/lib/shoes/drawables/progress.rb,
lacci/lib/shoes/drawables/edit_line.rb,
lacci/lib/shoes/drawables/document_root.rb,
lacci/lib/shoes/drawables/text_drawable.rb
Overview
Lacci Shoes apps operate in multiple layers. A Shoes drawable tree exists as fairly plain, simple Ruby objects. And then a display-service drawable tree integrates with the display technology. This lets us use Ruby as our API while not tying it too closely to the limitations of Webview, WASM, LibUI, etc.
Choosing Display Services
Before running a Lacci app, you can set SCARPE_DISPLAY_SERVICE. If you set it to "whatever_service", Scarpe will require "scarpe/whatever_service", which can be supplied by the Scarpe gem or another Scarpe-based gem. Currently leaving the environment variable empty is equivalent to requesting local Webview.
Events
Events are a lot of what tie the Shoes drawables and the display service together.
Shoes drawables expect to operate in a fairly "hands off" mode where they record to an event queue to send to the display service, and the display service records events to send back.
When a Shoes handler takes an action (e.g. some_para.replace(),) the relevant call will be dispatched as a :display event, to be sent to the display service. And when a display-side event occurs (e.g. user pushes a button,) it will be dispatched as a :shoes event, to be sent to the Shoes tree of drawables.
Defined Under Namespace
Modules: Background, Builtins, Colors, Constants, Errors, Log, Spec Classes: App, Arc, Arrow, Border, Button, Changelog, Check, DisplayService, DocumentRoot, Drawable, EditBox, EditLine, Error, Flow, Image, Line, Link, LinkHover, Linkable, ListBox, LoggedWrapper, Oval, Para, Progress, Radio, Rect, Shape, Slot, SpecInstance, SpecProxy, Stack, Star, SubscriptionItem, TextDrawable, Video, Widget
Constant Summary collapse
- LOG_LEVELS =
[:debug, :info, :warn, :error, :fatal].freeze
- RELEASE_INFO =
changelog_instance.get_latest_release_info
- RELEASE_NAME =
- RELEASE_ID =
- RELEASE_BUILD_DATE =
- RELEASE_TYPE =
This isn't really a thing any more
"LOOSE_SHOES"- REVISION =
Class Attribute Summary collapse
-
.APPS ⇒ Object
Returns the value of attribute APPS.
-
.pending_app_class ⇒ Object
Track the most recently defined Shoes subclass for the inheritance pattern e.g., class Book < Shoes; end; Shoes.app.
Class Method Summary collapse
- .add_file_loader(loader) ⇒ Object
-
.app(title: 'Shoes!', width: 480, height: 420, resizable: true, features: [], &app_code_body) ⇒ void
Creates a Shoes app with a new window.
-
.class_routes ⇒ Object
Get the routes defined on this class.
- .default_file_loaders ⇒ Object
- .default_text_drawable_with(element) ⇒ Object
- .file_loaders ⇒ Object
-
.inherited(subclass) ⇒ Object
When someone does
class MyApp < Shoes, track it. - .reset_file_loaders ⇒ Object
-
.run_app(relative_path) ⇒ void
Load a Shoes app from a file.
- .set_file_loaders(loaders) ⇒ Object
-
.url(path, method_name) ⇒ Object
Class-level url method for defining routes in Shoes subclasses e.g., class Book < Shoes; url '/', :index; end.
Class Attribute Details
.APPS ⇒ Object
Returns the value of attribute APPS.
58 59 60 |
# File 'lacci/lib/shoes.rb', line 58 def APPS @APPS end |
.pending_app_class ⇒ Object
Track the most recently defined Shoes subclass for the inheritance pattern e.g., class Book < Shoes; end; Shoes.app
62 63 64 |
# File 'lacci/lib/shoes.rb', line 62 def pending_app_class @pending_app_class end |
Class Method Details
.add_file_loader(loader) ⇒ Object
211 212 213 |
# File 'lacci/lib/shoes.rb', line 211 def add_file_loader(loader) file_loaders.prepend(loader) end |
.app(title: 'Shoes!', width: 480, height: 420, resizable: true, features: [], &app_code_body) ⇒ void
This method returns an undefined value.
Creates a Shoes app with a new window. The block parameter is used to create drawables and set up handlers. Arguments are passed to Shoes::App.new internally.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lacci/lib/shoes.rb', line 114 def app( title: 'Shoes!', width: 480, height: 420, resizable: true, features: [], &app_code_body ) f = [features].flatten # Make sure this is a list, not a single symbol app = Shoes::App.new(title:, width:, height:, resizable:, features: f, &app_code_body) # If there's a pending Shoes subclass (e.g., class Book < Shoes), use it if Shoes.pending_app_class subclass = Shoes.pending_app_class Shoes.pending_app_class = nil # Clear it so it doesn't affect future apps # Include the subclass as a module to get its instance methods # This works because we're extending the singleton class methods_to_copy = subclass.instance_methods(false) methods_to_copy.each do |method_name| # Get source location and use eval to redefine - but that's fragile # Instead, let's use a delegation pattern with the app as context # Read the method's arity and create a proper wrapper um = subclass.instance_method(method_name) # Define a wrapper that will eval the original method body in app's context # This is a bit hacky but works: we store the subclass and call via instance_eval app.define_singleton_method(method_name) do |*args, &block| # Create a temporary subclass instance that delegates to app for Shoes methods temp = subclass.allocate temp.instance_variable_set(:@__shoes_app__, self) # Define method_missing on the temp to delegate Shoes DSL calls to the app temp.define_singleton_method(:method_missing) do |name, *a, **kw, &b| @__shoes_app__.send(name, *a, **kw, &b) end temp.define_singleton_method(:respond_to_missing?) { |*| true } # Call the original method on temp (which delegates DSL calls to app) temp.send(method_name, *args, &block) end end # Copy routes from the subclass to the app subclass.class_routes.each do |path, method_name| app.url(path, method_name) end end app.init app.run nil end |
.class_routes ⇒ Object
Get the routes defined on this class
88 89 90 |
# File 'lacci/lib/shoes.rb', line 88 def class_routes @class_routes ||= {} end |
.default_file_loaders ⇒ Object
197 198 199 200 201 202 203 204 205 |
# File 'lacci/lib/shoes.rb', line 197 def default_file_loaders [ # By default we will always try to load any file, regardless of extension, as a Shoes Ruby file. proc do |path| load path true end ] end |
.default_text_drawable_with(element) ⇒ Object
98 99 100 101 102 103 104 105 106 107 |
# File 'lacci/lib/shoes/drawables/text_drawable.rb', line 98 def default_text_drawable_with(element) class_name = element.capitalize drawable_class = Class.new(Shoes::TextDrawable) do shoes_events # No specific events init_args # We're going to pass an empty array to super end Shoes.const_set class_name, drawable_class end |
.file_loaders ⇒ Object
207 208 209 |
# File 'lacci/lib/shoes.rb', line 207 def file_loaders @file_loaders ||= default_file_loaders end |
.inherited(subclass) ⇒ Object
When someone does class MyApp < Shoes, track it
65 66 67 68 69 70 71 72 |
# File 'lacci/lib/shoes.rb', line 65 def inherited(subclass) # Only track direct subclasses of Shoes, not Shoes::App, Shoes::Drawable, etc. # Those have their own inheritance tracking if self == ::Shoes Shoes.pending_app_class = subclass end super end |
.reset_file_loaders ⇒ Object
215 216 217 |
# File 'lacci/lib/shoes.rb', line 215 def reset_file_loaders @file_loaders = default_file_loaders end |
.run_app(relative_path) ⇒ void
This method returns an undefined value.
Load a Shoes app from a file. By default, this will load old-style Shoes apps from a .rb file with all the appropriate libraries loaded. By setting one or more loaders, a Lacci-based display library can accept new file formats as well, not just raw Shoes .rb files.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lacci/lib/shoes.rb', line 178 def run_app(relative_path) path = File. relative_path dir = File.dirname(path) # Shoes assumes we're starting from the app code's path Dir.chdir(dir) loaded = false file_loaders.each do |loader| if loader.call(path) loaded = true break end end raise "Could not find a file loader for #{path.inspect}!" unless loaded nil end |
.set_file_loaders(loaders) ⇒ Object
219 220 221 |
# File 'lacci/lib/shoes.rb', line 219 def set_file_loaders(loaders) @file_loaders = loaders end |
.url(path, method_name) ⇒ Object
Class-level url method for defining routes in Shoes subclasses e.g., class Book < Shoes; url '/', :index; end
76 77 78 79 80 81 82 83 84 85 |
# File 'lacci/lib/shoes.rb', line 76 def url(path, method_name) @class_routes ||= {} if path.is_a?(String) && path.include?('(') # Convert string patterns like '/page/(\d+)' to regex regex = Regexp.new("^#{path.gsub(/\(.*?\)/, '(.*?)')}$") @class_routes[regex] = method_name else @class_routes[path] = method_name end end |