Class: Ruflet::Page
- Inherits:
-
Object
- Object
- Ruflet::Page
- Defined in:
- lib/ruflet_ui/ruflet/page.rb
Defined Under Namespace
Classes: FlashlightService, ScreenBrightnessService, SharedPreferencesService, WakelockService
Constant Summary collapse
- PAGE_PROP_KEYS =
%w[dark_theme fonts route rtl show_semantics_debugger theme theme_mode theme_animation_style title vertical_alignment horizontal_alignment scroll].freeze
- DIALOG_PROP_KEYS =
%w[dialog snack_bar bottom_sheet].freeze
- PAGE_ADD_RESERVED_KEYS =
%i[appbar bottom_appbar floating_action_button navigation_bar dialog snack_bar bottom_sheet].freeze
- WIDGET_HELPER_METHODS =
( Ruflet::UI::MaterialControlMethods.instance_methods(false) + Ruflet::UI::CupertinoControlMethods.instance_methods(false) + %i[control widget] ).map(&:to_s).uniq.freeze
- RELOAD_PRESERVED_PAGE_PROPS =
Page props that must survive a hot reload: the route (kept on purpose), the client-reported window, and the internal overlay/services/dialogs containers (kept mounted so the client keeps their bindings).
%w[route window _overlay _dialogs _services].freeze
Instance Attribute Summary collapse
-
#client_details ⇒ Object
readonly
Returns the value of attribute client_details.
-
#session_id ⇒ Object
readonly
Returns the value of attribute session_id.
-
#views ⇒ Object
Returns the value of attribute views.
-
#window ⇒ Object
readonly
Returns the value of attribute window.
Instance Method Summary collapse
- #accelerometer(**props) ⇒ Object
- #add(control) ⇒ Object
- #add_service(*value) ⇒ Object
- #appbar ⇒ Object
- #appbar=(value) ⇒ Object
- #apply_client_update(control_or_id, props) ⇒ Object
- #audio(**props) ⇒ Object
- #audio_recorder(**props) ⇒ Object
- #auto_scroll ⇒ Object
- #auto_scroll=(value) ⇒ Object
- #barometer(**props) ⇒ Object
- #battery(**props) ⇒ Object
- #battery_save_mode?(timeout: nil, on_result: nil) ⇒ Boolean
- #bgcolor ⇒ Object
- #bgcolor=(value) ⇒ Object
- #bottom_appbar ⇒ Object
- #bottom_appbar=(value) ⇒ Object
- #bottom_sheet ⇒ Object
- #bottom_sheet=(value) ⇒ Object
- #bottomappbar=(value) ⇒ Object
- #bottomsheet=(value) ⇒ Object
- #browser_context_menu ⇒ Object
- #browser_context_menu=(value) ⇒ Object
- #camera(**props) ⇒ Object
- #can_launch_url(url, timeout: 10) ⇒ Object
- #clean ⇒ Object
- #clipboard(**props) ⇒ Object
- #close(dialog_control = nil) ⇒ Object
- #close_banner(banner_control = nil) ⇒ Object
- #close_bottom_sheet(bottom_sheet_control = nil) ⇒ Object
- #close_bottomsheet(bottom_sheet_control = nil) ⇒ Object
- #close_dialog(dialog_control = nil) ⇒ Object
- #close_drawer(timeout: 10, on_result: nil) ⇒ Object
- #close_end_drawer(timeout: 10, on_result: nil) ⇒ Object
- #close_in_app_web_view(timeout: 10, on_result: nil) ⇒ Object
- #connectivity(**props) ⇒ Object
- #controls ⇒ Object
- #controls=(value) ⇒ Object
- #decoration ⇒ Object
- #decoration=(value) ⇒ Object
- #dialog ⇒ Object
- #dialog=(value) ⇒ Object
- #dispatch_event(target:, name:, data:) ⇒ Object
- #drawer ⇒ Object
- #drawer=(value) ⇒ Object
- #end_drawer ⇒ Object
- #end_drawer=(value) ⇒ Object
- #file_picker(**props) ⇒ Object
-
#finalize_reload! ⇒ Object
Called after the reloaded app block ran, before the reload view patch.
- #flashlight(**props) ⇒ Object
- #floating_action_button ⇒ Object
- #floating_action_button=(value) ⇒ Object
- #floating_action_button_location ⇒ Object
- #floating_action_button_location=(value) ⇒ Object
- #foreground_decoration ⇒ Object
- #foreground_decoration=(value) ⇒ Object
- #geolocator(**props) ⇒ Object
- #get_application_cache_directory(timeout: nil, on_result: nil) ⇒ Object
- #get_application_documents_directory(timeout: nil, on_result: nil) ⇒ Object
- #get_application_support_directory(timeout: nil, on_result: nil) ⇒ Object
- #get_battery_level(timeout: nil, on_result: nil) ⇒ Object
- #get_battery_state(timeout: nil, on_result: nil) ⇒ Object
- #get_clipboard(timeout: nil, on_result: nil) ⇒ Object
- #get_clipboard_files(timeout: nil, on_result: nil) ⇒ Object
- #get_clipboard_image(timeout: nil, on_result: nil) ⇒ Object
- #get_connectivity(timeout: nil, on_result: nil) ⇒ Object
- #get_console_log_filename(timeout: nil, on_result: nil) ⇒ Object
- #get_control(id) ⇒ Object
- #get_directory_path(dialog_title: nil, initial_directory: nil, timeout: nil, on_result: nil) ⇒ Object
- #get_downloads_directory(timeout: nil, on_result: nil) ⇒ Object
- #get_external_cache_directories(timeout: nil, on_result: nil) ⇒ Object
- #get_external_storage_directories(timeout: nil, on_result: nil) ⇒ Object
- #get_external_storage_directory(timeout: nil, on_result: nil) ⇒ Object
- #get_library_directory(timeout: nil, on_result: nil) ⇒ Object
- #get_temporary_directory(timeout: nil, on_result: nil) ⇒ Object
- #go(route, **query_params) ⇒ Object
- #gyroscope(**props) ⇒ Object
- #handle_invoke_method_result(payload) ⇒ Object
- #haptic_feedback(**props) ⇒ Object
- #heavy_impact(timeout: 10, on_result: nil) ⇒ Object
- #horizontal_alignment ⇒ Object
- #horizontal_alignment=(value) ⇒ Object
-
#initialize(session_id:, client_details:, sender:) ⇒ Page
constructor
A new instance of Page.
- #insert(at, *controls) ⇒ Object
- #invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil) ⇒ Object
-
#invoke_sync(control_or_id, method_name, args: nil, timeout: 10) ⇒ Object
Synchronous invoke for controls/services that must return a value before continuing (e.g. picker selection, camera discovery/init).
- #is_in_battery_save_mode(timeout: nil, on_result: nil) ⇒ Object
- #launch_url(url, mode: nil, web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10, on_result: nil) ⇒ Object
- #light_impact(timeout: 10, on_result: nil) ⇒ Object
- #magnetometer(**props) ⇒ Object
- #medium_impact(timeout: 10, on_result: nil) ⇒ Object
- #method_missing(name, *args, &block) ⇒ Object
- #mount(&block) ⇒ Object
- #navigate(route, **query_params) ⇒ Object
- #navigation_bar ⇒ Object
- #navigation_bar=(value) ⇒ Object
- #on(event_name, &block) ⇒ Object
- #on_route_change=(handler) ⇒ Object
- #on_view_pop=(handler) ⇒ Object
- #on_views_pop_until=(handler) ⇒ Object
-
#open(dialog_control) ⇒ Object
Flet-compatible generic overlay API.
- #open_window(url, title: nil, width: nil, height: nil, timeout: 10, on_result: nil) ⇒ Object
- #overlay ⇒ Object
- #overlay=(value) ⇒ Object
- #padding ⇒ Object
- #padding=(value) ⇒ Object
- #patch_page(control_id, **props) ⇒ Object
- #permission_handler(**props) ⇒ Object
-
#pick_files(dialog_title: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, allow_multiple: false, with_data: false, timeout: nil, on_result: nil) ⇒ Object
File picker helpers: create an ephemeral service, invoke method, and dispose it.
- #pop_dialog ⇒ Object
- #push_route(route, **query_params) ⇒ Object
- #query ⇒ Object
-
#register_page_patch ⇒ Object
Complete page state as a property map for the register_client response (Flet's page_patch).
- #remove(*controls) ⇒ Object
- #remove_at(index) ⇒ Object
- #remove_service(*value) ⇒ Object
-
#replay_route_after_reload! ⇒ Object
Called after the reloaded app block ran.
-
#reset_for_reload! ⇒ Object
Quietly clears session content and page chrome so a reloaded app block can re-render on this same Page without recreating it.
- #respond_to_missing?(name, include_private = false) ⇒ Boolean
- #route ⇒ Object
- #route=(value) ⇒ Object
- #save_file(dialog_title: nil, file_name: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, src_bytes: nil, timeout: nil, on_result: nil) ⇒ Object
- #schedule_update ⇒ Object
- #screen_brightness(**props) ⇒ Object
- #screenshot(**props) ⇒ Object
- #scroll_to(offset: nil, delta: nil, scroll_key: nil, duration: nil, curve: nil, timeout: 10, on_result: nil) ⇒ Object
- #secure_storage(**props) ⇒ Object
- #selection_click(timeout: 10, on_result: nil) ⇒ Object
- #semantics_service(**props) ⇒ Object
- #service(type, **props) ⇒ Object
- #services ⇒ Object
- #services=(value) ⇒ Object
- #set_clipboard(value, timeout: nil, on_result: nil) ⇒ Object
- #set_clipboard_files(files, timeout: nil, on_result: nil) ⇒ Object
- #set_clipboard_image(value, timeout: nil, on_result: nil) ⇒ Object
- #set_view_props(props) ⇒ Object
- #shake_detector(**props) ⇒ Object
- #share(**props) ⇒ Object
- #share_files(files = nil, text: nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object
- #share_text(text = nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object
- #share_uri(uri = nil, share_position_origin: nil, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object
- #shared_preferences(**props) ⇒ Object
- #show_banner(banner_control) ⇒ Object
- #show_bottom_sheet(bottom_sheet_control) ⇒ Object
- #show_bottomsheet(bottom_sheet_control) ⇒ Object
- #show_dialog(dialog_control) ⇒ Object
- #show_drawer(timeout: 10, on_result: nil) ⇒ Object
- #show_end_drawer(timeout: 10, on_result: nil) ⇒ Object
- #show_snack_bar(snack_bar_control) ⇒ Object
- #show_snackbar(snack_bar_control) ⇒ Object
- #snack_bar ⇒ Object
- #snack_bar=(value) ⇒ Object
- #snackbar=(value) ⇒ Object
- #spacing ⇒ Object
- #spacing=(value) ⇒ Object
- #storage_paths(**props) ⇒ Object
- #supports_close_for_launch_mode(mode, timeout: 10, on_result: nil) ⇒ Object
- #supports_launch_mode(mode, timeout: 10, on_result: nil) ⇒ Object
- #title ⇒ Object
- #title=(value) ⇒ Object
- #update(control_or_id = nil, **props) ⇒ Object
- #upload(files, timeout: nil, on_result: nil) ⇒ Object
- #upload_files(files, timeout: nil, on_result: nil) ⇒ Object
- #url_launcher(**props) ⇒ Object
- #user_accelerometer(**props) ⇒ Object
- #vertical_alignment ⇒ Object
- #vertical_alignment=(value) ⇒ Object
- #vibrate(timeout: 10, on_result: nil) ⇒ Object
- #wakelock(**props) ⇒ Object
-
#web ⇒ Object
(also: #web?)
Older and embedded clients may omit this capability from registration.
Constructor Details
#initialize(session_id:, client_details:, sender:) ⇒ Page
Returns a new instance of Page.
163 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 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 163 def initialize(session_id:, client_details:, sender:) @session_id = session_id @client_details = client_details @sender = sender @control_index = {} @wire_index = {} @next_wire_id = 100 @view_id = 20 @root_controls = [] @views = [] @dialogs = [] @overlay_container_mounted = false @dialogs_container_mounted = false @services_container_mounted = false @visual_service_controls = {} @page_event_handlers = {} @view_props = {} @page_props = { "route" => (client_details["route"] || "/") } @window = build_client_window(client_details["window"]) @page_props["window"] = @window @overlay_container = Ruflet::Control.new( type: "overlay", id: "_overlay", controls: [] ) @services_container = Ruflet::Control.new( type: "service_registry", id: "_services", "_services": [], "_internals": { "uid" => Ruflet::Control.generate_id } ) @dialogs_container = Ruflet::Control.new( type: "dialogs", id: "_dialogs", controls: [] ) @invoke_waiters = {} @invoke_callbacks = {} @invoke_waiters_mutex = Mutex.new @shared_preferences_proxy = SharedPreferencesService.new(self) @wakelock_proxy = WakelockService.new(self) @flashlight_proxy = FlashlightService.new(self) @screen_brightness_proxy = ScreenBrightnessService.new(self) refresh_services_container! refresh_dialogs_container! end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1373 def method_missing(name, *args, &block) method_name = name.to_s prop_name = method_name.delete_suffix("=") if method_name.end_with?("=") if (prop_name) raise NoMethodError, "Use `#{prop_name}(...)` as a free widget helper, then attach with `page.add(...)`." end assign_split_prop(prop_name, normalize_value(prop_name, args.first)) return args.first end if args.empty? && !block return @page_props[method_name] if @page_props.key?(method_name) return @view_props[method_name] if @view_props.key?(method_name) # Client-reported page properties (width, height, platform, # platform_brightness, media) arrive in the register payload. return @client_details[method_name] if @client_details.respond_to?(:key?) && @client_details.key?(method_name) return instance_variable_get("@#{method_name}") if DIALOG_PROP_KEYS.include?(method_name) end if (name) raise NoMethodError, "Use `#{name}(...)` as a free widget helper, then attach with `page.add(...)`." end super end |
Instance Attribute Details
#client_details ⇒ Object (readonly)
Returns the value of attribute client_details.
161 162 163 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 161 def client_details @client_details end |
#session_id ⇒ Object (readonly)
Returns the value of attribute session_id.
161 162 163 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 161 def session_id @session_id end |
#views ⇒ Object
Returns the value of attribute views.
161 162 163 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 161 def views @views end |
#window ⇒ Object (readonly)
Returns the value of attribute window.
161 162 163 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 161 def window @window end |
Instance Method Details
#accelerometer(**props) ⇒ Object
1055 1056 1057 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1055 def accelerometer(**props) service(:accelerometer, **props) end |
#add(control) ⇒ Object
260 261 262 263 264 265 266 267 268 269 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 260 def add(control) if control.is_a?(Hash) && (control.keys.map(&:to_sym) & PAGE_ADD_RESERVED_KEYS).any? raise ArgumentError, "Page#add accepts only controls; assign page.appbar, page.floating_action_button, dialogs, or other page properties before calling add" end raise ArgumentError, "Page#add accepts exactly one control" unless control.is_a?(Control) replace_root_controls(@root_controls + [control]) self end |
#add_service(*value) ⇒ Object
450 451 452 453 454 455 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 450 def add_service(*value) @services_container.props["_services"] = services + value.flatten.compact refresh_services_container! push_services_update! self end |
#appbar ⇒ Object
564 565 566 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 564 def @view_props["appbar"] end |
#appbar=(value) ⇒ Object
568 569 570 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 568 def (value) @view_props["appbar"] = value end |
#apply_client_update(control_or_id, props) ⇒ Object
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1338 def apply_client_update(control_or_id, props) control = resolve_control(control_or_id) return self unless control patch = normalize_props(props || {}) patch.each { |k, v| control.props[k] = v } remove_dialog_tracking(control) if patch.key?("open") && patch["open"] == false self end |
#audio(**props) ⇒ Object
442 443 444 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 442 def audio(**props) service(:audio, **props) end |
#audio_recorder(**props) ⇒ Object
446 447 448 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 446 def audio_recorder(**props) service(:audio_recorder, **props) end |
#auto_scroll ⇒ Object
600 601 602 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 600 def auto_scroll @view_props["auto_scroll"] end |
#auto_scroll=(value) ⇒ Object
604 605 606 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 604 def auto_scroll=(value) @view_props["auto_scroll"] = value end |
#barometer(**props) ⇒ Object
1071 1072 1073 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1071 def (**props) service(:barometer, **props) end |
#battery(**props) ⇒ Object
1099 1100 1101 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1099 def battery(**props) service(:battery, **props) end |
#battery_save_mode?(timeout: nil, on_result: nil) ⇒ Boolean
1051 1052 1053 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1051 def battery_save_mode?(timeout: nil, on_result: nil) is_in_battery_save_mode(timeout: timeout, on_result: on_result) end |
#bgcolor ⇒ Object
252 253 254 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 252 def bgcolor @view_props["bgcolor"] end |
#bgcolor=(value) ⇒ Object
256 257 258 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 256 def bgcolor=(value) @view_props["bgcolor"] = normalize_value("bgcolor", value) end |
#bottom_appbar ⇒ Object
572 573 574 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 572 def @view_props["bottom_appbar"] end |
#bottom_appbar=(value) ⇒ Object
576 577 578 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 576 def (value) @view_props["bottom_appbar"] = value end |
#bottom_sheet ⇒ Object
700 701 702 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 700 def bottom_sheet @bottom_sheet end |
#bottom_sheet=(value) ⇒ Object
694 695 696 697 698 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 694 def bottom_sheet=(value) @bottom_sheet = value refresh_dialogs_container! push_dialogs_update! if @dialogs_container_mounted end |
#bottomappbar=(value) ⇒ Object
580 581 582 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 580 def (value) self. = value end |
#bottomsheet=(value) ⇒ Object
704 705 706 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 704 def bottomsheet=(value) self.bottom_sheet = value end |
#browser_context_menu ⇒ Object
608 609 610 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 608 def @view_props["browser_context_menu"] end |
#browser_context_menu=(value) ⇒ Object
612 613 614 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 612 def (value) @view_props["browser_context_menu"] = value end |
#camera(**props) ⇒ Object
1127 1128 1129 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1127 def camera(**props) service(:camera, **props) end |
#can_launch_url(url, timeout: 10) ⇒ Object
871 872 873 874 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 871 def can_launch_url(url, timeout: 10) url_launcher = ensure_url_launcher_service invoke(url_launcher, "can_launch_url", args: { "url" => url }, timeout: timeout) end |
#clean ⇒ Object
298 299 300 301 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 298 def clean replace_root_controls([]) self end |
#clipboard(**props) ⇒ Object
1107 1108 1109 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1107 def clipboard(**props) service(:clipboard, **props) end |
#close(dialog_control = nil) ⇒ Object
760 761 762 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 760 def close(dialog_control = nil) close_dialog(dialog_control) end |
#close_banner(banner_control = nil) ⇒ Object
750 751 752 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 750 def ( = nil) close_dialog() end |
#close_bottom_sheet(bottom_sheet_control = nil) ⇒ Object
738 739 740 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 738 def close_bottom_sheet(bottom_sheet_control = nil) close_dialog(bottom_sheet_control || @bottom_sheet) end |
#close_bottomsheet(bottom_sheet_control = nil) ⇒ Object
742 743 744 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 742 def close_bottomsheet(bottom_sheet_control = nil) close_bottom_sheet(bottom_sheet_control) end |
#close_dialog(dialog_control = nil) ⇒ Object
764 765 766 767 768 769 770 771 772 773 774 775 776 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 764 def close_dialog(dialog_control = nil) target = dialog_control || latest_open_dialog return nil unless target clear_dialog_slots(target) # The client pops a dialog's route only when the mounted control sees # its `open` prop flip to false (alert_dialog.dart tracks open/_open). # Replacing the dialogs container recreates the control client-side and # the transition is lost, so patch the open prop on the control itself. target.props["open"] = false update(target, open: false) target end |
#close_drawer(timeout: 10, on_result: nil) ⇒ Object
784 785 786 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 784 def close_drawer(timeout: 10, on_result: nil) invoke(:page, "close_drawer", timeout: timeout, on_result: on_result) end |
#close_end_drawer(timeout: 10, on_result: nil) ⇒ Object
794 795 796 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 794 def close_end_drawer(timeout: 10, on_result: nil) invoke(:page, "close_end_drawer", timeout: timeout, on_result: on_result) end |
#close_in_app_web_view(timeout: 10, on_result: nil) ⇒ Object
876 877 878 879 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 876 def close_in_app_web_view(timeout: 10, on_result: nil) url_launcher = ensure_url_launcher_service invoke(url_launcher, "close_in_app_web_view", timeout: timeout, on_result: on_result) end |
#connectivity(**props) ⇒ Object
1103 1104 1105 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1103 def connectivity(**props) service(:connectivity, **props) end |
#controls ⇒ Object
271 272 273 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 271 def controls @root_controls end |
#controls=(value) ⇒ Object
275 276 277 278 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 275 def controls=(value) replace_root_controls(Array(value).flatten.compact) self end |
#decoration ⇒ Object
616 617 618 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 616 def decoration @view_props["decoration"] end |
#decoration=(value) ⇒ Object
620 621 622 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 620 def decoration=(value) @view_props["decoration"] = value end |
#dialog ⇒ Object
672 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 672 def dialog = @dialog |
#dialog=(value) ⇒ Object
674 675 676 677 678 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 674 def dialog=(value) @dialog = value refresh_dialogs_container! push_dialogs_update! if @dialogs_container_mounted end |
#dispatch_event(target:, name:, data:) ⇒ Object
1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1350 def dispatch_event(target:, name:, data:) if page_control_target?(target) if name.to_s == "route_change" route_from_event = extract_route(data) return if route_from_event && route_from_event == @page_props["route"] @page_props["route"] = route_from_event if route_from_event end dispatch_page_event(name: name, data: data) return end control = @wire_index[target.to_i] || @control_index[target.to_s] return unless control event = Event.new(name: name, target: target, raw_data: data, page: self, control: control) apply_event_value_to_control(control, event) if %w[change select select_change].include?(name.to_s) control.emit(name, event) if name.to_s == "dismiss" && remove_dialog_tracking(control) push_dialogs_update! end end |
#drawer ⇒ Object
656 657 658 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 656 def drawer @view_props["drawer"] end |
#drawer=(value) ⇒ Object
660 661 662 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 660 def drawer=(value) @view_props["drawer"] = value end |
#end_drawer ⇒ Object
664 665 666 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 664 def end_drawer @view_props["end_drawer"] end |
#end_drawer=(value) ⇒ Object
668 669 670 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 668 def end_drawer=(value) @view_props["end_drawer"] = value end |
#file_picker(**props) ⇒ Object
1111 1112 1113 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1111 def file_picker(**props) service(:file_picker, **props) end |
#finalize_reload! ⇒ Object
Called after the reloaded app block ran, before the reload view patch. Page-level props merge on the client (the Page control is not recreated), so chrome the reloaded block dropped must be sent as an explicit nil or it lingers. View props need no such treatment: the View is rebuilt wholesale from @view_props. Also flushes the overlay container, which stays mounted and is skipped by a plain view patch.
364 365 366 367 368 369 370 371 372 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 364 def finalize_reload! keys = @reload_prior_page_prop_keys if keys (keys - @page_props.keys).each { |key| @page_props[key] = nil } @reload_prior_page_prop_keys = nil end self end |
#flashlight(**props) ⇒ Object
430 431 432 433 434 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 430 def flashlight(**props) return service(:flashlight, **props) unless props.empty? @flashlight_proxy end |
#floating_action_button ⇒ Object
584 585 586 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 584 def @view_props["floating_action_button"] end |
#floating_action_button=(value) ⇒ Object
588 589 590 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 588 def (value) @view_props["floating_action_button"] = value end |
#floating_action_button_location ⇒ Object
624 625 626 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 624 def @view_props["floating_action_button_location"] end |
#floating_action_button_location=(value) ⇒ Object
628 629 630 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 628 def (value) @view_props["floating_action_button_location"] = value end |
#foreground_decoration ⇒ Object
632 633 634 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 632 def foreground_decoration @view_props["foreground_decoration"] end |
#foreground_decoration=(value) ⇒ Object
636 637 638 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 636 def foreground_decoration=(value) @view_props["foreground_decoration"] = value end |
#geolocator(**props) ⇒ Object
1075 1076 1077 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1075 def geolocator(**props) service(:geolocator, **props) end |
#get_application_cache_directory(timeout: nil, on_result: nil) ⇒ Object
1135 1136 1137 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1135 def get_application_cache_directory(timeout: nil, on_result: nil) invoke_storage_paths("get_application_cache_directory", timeout: timeout, on_result: on_result) end |
#get_application_documents_directory(timeout: nil, on_result: nil) ⇒ Object
1139 1140 1141 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1139 def get_application_documents_directory(timeout: nil, on_result: nil) invoke_storage_paths("get_application_documents_directory", timeout: timeout, on_result: on_result) end |
#get_application_support_directory(timeout: nil, on_result: nil) ⇒ Object
1143 1144 1145 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1143 def get_application_support_directory(timeout: nil, on_result: nil) invoke_storage_paths("get_application_support_directory", timeout: timeout, on_result: on_result) end |
#get_battery_level(timeout: nil, on_result: nil) ⇒ Object
1039 1040 1041 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1039 def get_battery_level(timeout: nil, on_result: nil) invoke_battery_method("get_battery_level", timeout: timeout, on_result: on_result) end |
#get_battery_state(timeout: nil, on_result: nil) ⇒ Object
1043 1044 1045 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1043 def get_battery_state(timeout: nil, on_result: nil) invoke_battery_method("get_battery_state", timeout: timeout, on_result: on_result) end |
#get_clipboard(timeout: nil, on_result: nil) ⇒ Object
1005 1006 1007 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1005 def get_clipboard(timeout: nil, on_result: nil) invoke_clipboard_method("get", timeout: timeout, on_result: on_result) end |
#get_clipboard_files(timeout: nil, on_result: nil) ⇒ Object
1018 1019 1020 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1018 def get_clipboard_files(timeout: nil, on_result: nil) invoke_clipboard_method("get_files", timeout: timeout, on_result: on_result) end |
#get_clipboard_image(timeout: nil, on_result: nil) ⇒ Object
1031 1032 1033 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1031 def get_clipboard_image(timeout: nil, on_result: nil) invoke_clipboard_method("get_image", timeout: timeout, on_result: on_result) end |
#get_connectivity(timeout: nil, on_result: nil) ⇒ Object
1035 1036 1037 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1035 def get_connectivity(timeout: nil, on_result: nil) invoke_connectivity_method("get_connectivity", timeout: timeout, on_result: on_result) end |
#get_console_log_filename(timeout: nil, on_result: nil) ⇒ Object
1171 1172 1173 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1171 def get_console_log_filename(timeout: nil, on_result: nil) invoke_storage_paths("get_console_log_filename", timeout: timeout, on_result: on_result) end |
#get_control(id) ⇒ Object
397 398 399 400 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 397 def get_control(id) refresh_control_indexes! resolve_control(id) end |
#get_directory_path(dialog_title: nil, initial_directory: nil, timeout: nil, on_result: nil) ⇒ Object
951 952 953 954 955 956 957 958 959 960 961 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 951 def get_directory_path(dialog_title: nil, initial_directory: nil, timeout: nil, on_result: nil) invoke_file_picker( "get_directory_path", compact_service_args( "dialog_title" => dialog_title, "initial_directory" => initial_directory ), timeout: timeout, on_result: on_result ) end |
#get_downloads_directory(timeout: nil, on_result: nil) ⇒ Object
1147 1148 1149 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1147 def get_downloads_directory(timeout: nil, on_result: nil) invoke_storage_paths("get_downloads_directory", timeout: timeout, on_result: on_result) end |
#get_external_cache_directories(timeout: nil, on_result: nil) ⇒ Object
1151 1152 1153 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1151 def get_external_cache_directories(timeout: nil, on_result: nil) invoke_storage_paths("get_external_cache_directories", timeout: timeout, on_result: on_result) end |
#get_external_storage_directories(timeout: nil, on_result: nil) ⇒ Object
1155 1156 1157 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1155 def get_external_storage_directories(timeout: nil, on_result: nil) invoke_storage_paths("get_external_storage_directories", timeout: timeout, on_result: on_result) end |
#get_external_storage_directory(timeout: nil, on_result: nil) ⇒ Object
1163 1164 1165 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1163 def get_external_storage_directory(timeout: nil, on_result: nil) invoke_storage_paths("get_external_storage_directory", timeout: timeout, on_result: on_result) end |
#get_library_directory(timeout: nil, on_result: nil) ⇒ Object
1159 1160 1161 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1159 def get_library_directory(timeout: nil, on_result: nil) invoke_storage_paths("get_library_directory", timeout: timeout, on_result: on_result) end |
#get_temporary_directory(timeout: nil, on_result: nil) ⇒ Object
1167 1168 1169 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1167 def get_temporary_directory(timeout: nil, on_result: nil) invoke_storage_paths("get_temporary_directory", timeout: timeout, on_result: on_result) end |
#go(route, **query_params) ⇒ Object
520 521 522 523 524 525 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 520 def go(route, **query_params) @page_props["route"] = build_route(route, query_params) dispatch_page_event(name: "route_change", data: @page_props["route"]) send_view_patch self end |
#gyroscope(**props) ⇒ Object
1059 1060 1061 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1059 def gyroscope(**props) service(:gyroscope, **props) end |
#handle_invoke_method_result(payload) ⇒ Object
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1260 def handle_invoke_method_result(payload) call_id = payload["call_id"].to_s waiter = @invoke_waiters_mutex.synchronize { @invoke_waiters[call_id] } if waiter waiter << payload return true end callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(call_id) } return false unless callback callback.call(payload["result"], payload["error"]) true rescue StandardError => e Kernel.warn("invoke callback error: #{e.class}: #{e.}") false end |
#haptic_feedback(**props) ⇒ Object
1131 1132 1133 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1131 def haptic_feedback(**props) service(:haptic_feedback, **props) end |
#heavy_impact(timeout: 10, on_result: nil) ⇒ Object
976 977 978 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 976 def heavy_impact(timeout: 10, on_result: nil) invoke_haptic_feedback("heavy_impact", timeout: timeout, on_result: on_result) end |
#horizontal_alignment ⇒ Object
242 243 244 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 242 def horizontal_alignment @page_props["horizontal_alignment"] || @view_props["horizontal_alignment"] end |
#horizontal_alignment=(value) ⇒ Object
246 247 248 249 250 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 246 def horizontal_alignment=(value) v = normalize_value("horizontal_alignment", value) @page_props["horizontal_alignment"] = v @view_props["horizontal_alignment"] = v end |
#insert(at, *controls) ⇒ Object
280 281 282 283 284 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 280 def insert(at, *controls) @root_controls.insert(at.to_i, *controls.flatten.compact) send_view_patch self end |
#invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil) ⇒ Object
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 814 def invoke(control_or_id, method_name, args: nil, timeout: 10, on_result: nil) control_id = if page_control_target?(control_or_id) 1 else control = resolve_control(control_or_id) return nil unless control control.wire_id end call_id = "call_#{Ruflet::Control.generate_id}" if on_result.respond_to?(:call) @invoke_waiters_mutex.synchronize { @invoke_callbacks[call_id] = on_result } unless timeout.nil? Thread.new(call_id, timeout.to_f) do |pending_call_id, invoke_timeout| sleep([invoke_timeout, 0.0].max + 0.1) callback = @invoke_waiters_mutex.synchronize { @invoke_callbacks.delete(pending_call_id) } callback&.call(nil, "execution expired") rescue StandardError => e Kernel.warn("invoke timeout callback error: #{e.class}: #{e.}") end end end payload = { "control_id" => control_id, "call_id" => call_id, "name" => method_name.to_s, "args" => args } payload["timeout"] = timeout unless timeout.nil? (Protocol::ACTIONS[:invoke_control_method], payload) call_id end |
#invoke_sync(control_or_id, method_name, args: nil, timeout: 10) ⇒ Object
Synchronous invoke for controls/services that must return a value before continuing (e.g. picker selection, camera discovery/init).
851 852 853 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 851 def invoke_sync(control_or_id, method_name, args: nil, timeout: 10) invoke_and_wait(control_or_id, method_name, args: args, timeout: timeout) end |
#is_in_battery_save_mode(timeout: nil, on_result: nil) ⇒ Object
1047 1048 1049 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1047 def is_in_battery_save_mode(timeout: nil, on_result: nil) invoke_battery_method("is_in_battery_save_mode", timeout: timeout, on_result: on_result) end |
#launch_url(url, mode: nil, web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10, on_result: nil) ⇒ Object
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 855 def launch_url(url, mode: nil, web_view_configuration: nil, browser_configuration: nil, web_only_window_name: nil, timeout: 10, on_result: nil) url_launcher = ensure_url_launcher_service args = { "url" => url } args["mode"] = mode unless mode.nil? args["web_view_configuration"] = web_view_configuration unless web_view_configuration.nil? args["browser_configuration"] = browser_configuration unless browser_configuration.nil? args["web_only_window_name"] = web_only_window_name unless web_only_window_name.nil? invoke( url_launcher, "launch_url", args: args, timeout: timeout, on_result: on_result ) end |
#light_impact(timeout: 10, on_result: nil) ⇒ Object
984 985 986 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 984 def light_impact(timeout: 10, on_result: nil) invoke_haptic_feedback("light_impact", timeout: timeout, on_result: on_result) end |
#magnetometer(**props) ⇒ Object
1067 1068 1069 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1067 def magnetometer(**props) service(:magnetometer, **props) end |
#medium_impact(timeout: 10, on_result: nil) ⇒ Object
980 981 982 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 980 def medium_impact(timeout: 10, on_result: nil) invoke_haptic_feedback("medium_impact", timeout: timeout, on_result: on_result) end |
#mount(&block) ⇒ Object
558 559 560 561 562 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 558 def mount(&block) builder = WidgetBuilder.new builder.instance_eval(&block) builder.children.each { |control| add(control) } end |
#navigate(route, **query_params) ⇒ Object
527 528 529 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 527 def navigate(route, **query_params) go(route, **query_params) end |
#navigation_bar ⇒ Object
592 593 594 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 592 def @view_props["navigation_bar"] end |
#navigation_bar=(value) ⇒ Object
596 597 598 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 596 def (value) @view_props["navigation_bar"] = value end |
#on(event_name, &block) ⇒ Object
551 552 553 554 555 556 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 551 def on(event_name, &block) name = event_name.to_s name = name[3..-1] if name.start_with?("on_") @page_event_handlers[name] = block self end |
#on_route_change=(handler) ⇒ Object
539 540 541 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 539 def on_route_change=(handler) @page_event_handlers["route_change"] = handler end |
#on_view_pop=(handler) ⇒ Object
543 544 545 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 543 def on_view_pop=(handler) @page_event_handlers["view_pop"] = handler end |
#on_views_pop_until=(handler) ⇒ Object
547 548 549 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 547 def on_views_pop_until=(handler) @page_event_handlers["views_pop_until"] = handler end |
#open(dialog_control) ⇒ Object
Flet-compatible generic overlay API. Prefer close_bottom_sheet for sheets so the page's current bottom-sheet slot is handled explicitly.
756 757 758 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 756 def open(dialog_control) show_dialog(dialog_control) end |
#open_window(url, title: nil, width: nil, height: nil, timeout: 10, on_result: nil) ⇒ Object
881 882 883 884 885 886 887 888 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 881 def open_window(url, title: nil, width: nil, height: nil, timeout: 10, on_result: nil) url_launcher = ensure_url_launcher_service args = { "url" => url } args["title"] = title unless title.nil? args["width"] = width unless width.nil? args["height"] = height unless height.nil? invoke(url_launcher, "open_window", args: args, timeout: timeout, on_result: on_result) end |
#overlay ⇒ Object
387 388 389 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 387 def @overlay_container.children end |
#overlay=(value) ⇒ Object
391 392 393 394 395 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 391 def (value) @overlay_container.children.replace(Array(value).flatten.compact) self end |
#padding ⇒ Object
640 641 642 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 640 def padding @view_props["padding"] end |
#padding=(value) ⇒ Object
644 645 646 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 644 def padding=(value) @view_props["padding"] = value end |
#patch_page(control_id, **props) ⇒ Object
1334 1335 1336 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1334 def patch_page(control_id, **props) update(control_id, **props) end |
#permission_handler(**props) ⇒ Object
1079 1080 1081 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1079 def (**props) service(:permission_handler, **props) end |
#pick_files(dialog_title: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, allow_multiple: false, with_data: false, timeout: nil, on_result: nil) ⇒ Object
File picker helpers: create an ephemeral service, invoke method, and dispose it.
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 901 def pick_files( dialog_title: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, allow_multiple: false, with_data: false, timeout: nil, on_result: nil ) invoke_file_picker( "pick_files", compact_service_args( "dialog_title" => dialog_title, "initial_directory" => initial_directory, "file_type" => file_type, "allowed_extensions" => allowed_extensions, "allow_multiple" => allow_multiple, "with_data" => with_data ), timeout: timeout, on_result: on_result ) end |
#pop_dialog ⇒ Object
1278 1279 1280 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1278 def pop_dialog close_dialog end |
#push_route(route, **query_params) ⇒ Object
531 532 533 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 531 def push_route(route, **query_params) go(route, **query_params) end |
#query ⇒ Object
535 536 537 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 535 def query parse_query(route) end |
#register_page_patch ⇒ Object
Complete page state as a property map for the register_client response (Flet's page_patch). The client applies this through Control.update, which merges by control id and keeps existing instances alive — the only patch path that survives a re-register on a client with prior state (reconnect after a backend restart). The op-based view patch replaces control instances and detaches containers on such clients.
309 310 311 312 313 314 315 316 317 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 309 def register_page_patch refresh_control_indexes! patch = { "views" => build_view_patches } @page_props.each { |key, value| patch[key] = serialize_patch_value(value) } @overlay_container_mounted = true if @overlay_container.wire_id @dialogs_container_mounted = true if @dialogs_container.wire_id @services_container_mounted = true if @services_container.wire_id patch end |
#remove(*controls) ⇒ Object
286 287 288 289 290 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 286 def remove(*controls) controls.flatten.each { |control| @root_controls.delete(control) } send_view_patch self end |
#remove_at(index) ⇒ Object
292 293 294 295 296 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 292 def remove_at(index) @root_controls.delete_at(index.to_i) send_view_patch self end |
#remove_service(*value) ⇒ Object
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 457 def remove_service(*value) targets = value.flatten.compact return self if targets.empty? @services_container.props["_services"] = services.reject do |service| targets.any? do |target| case target when Control service.equal?(target) || (!target.id.nil? && service.id.to_s == target.id.to_s) else needle = target.to_s service.id.to_s == needle || service.type.to_s.downcase == needle.downcase end end end refresh_services_container! push_services_update! self end |
#replay_route_after_reload! ⇒ Object
Called after the reloaded app block ran. The route survives a reload (page props are kept), but the client only sends its route_change event at connect time — apps that render routes exclusively inside on_route_change would be stuck on stale content after a reload. Replay the event for them, unless the block already routed itself (page.go).
379 380 381 382 383 384 385 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 379 def replay_route_after_reload! return self if @route_change_seen_since_reset return self unless @page_event_handlers["route_change"].respond_to?(:call) dispatch_page_event(name: "route_change", data: @page_props["route"]) self end |
#reset_for_reload! ⇒ Object
Quietly clears session content and page chrome so a reloaded app block can re-render on this same Page without recreating it. The page object must survive a hot reload: a new Page re-sends the internal overlay/service/dialogs containers, which replaces their client-side instances and detaches them (see build_page_patch_ops) — after that, dialog and service patches are silently ignored by the client.
Chrome (appbar, drawer, FAB, bgcolor, title, ...) lives in @view_props and @page_props. If the reloaded block no longer sets a piece of chrome, the previous run's value would linger, so both are cleared here. The view is fully rebuilt on the next patch, which drops any @view_props not re-set; page-level props merge on the client, so finalize_reload! sends explicit nils for the ones that disappeared. Sends nothing itself.
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 337 def reset_for_reload! @root_controls = [] @views = [] @dialogs = [] @dialog = nil @snack_bar = nil @bottom_sheet = nil @route_change_seen_since_reset = false @reload_prior_page_prop_keys = @page_props.keys - RELOAD_PRESERVED_PAGE_PROPS @view_props = {} @page_props = @page_props.select { |key, _| RELOAD_PRESERVED_PAGE_PROPS.include?(key) } @page_props["route"] ||= (@client_details["route"] || "/") @page_props["window"] ||= @window @overlay_container.children.clear refresh_services_container! refresh_dialogs_container! self end |
#respond_to_missing?(name, include_private = false) ⇒ Boolean
1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1401 def respond_to_missing?(name, include_private = false) method_name = name.to_s prop_name = method_name.delete_suffix("=") (name) || (prop_name) || method_name.end_with?("=") || @page_props.key?(method_name) || @view_props.key?(method_name) || DIALOG_PROP_KEYS.include?(method_name) || super end |
#route ⇒ Object
224 225 226 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 224 def route @page_props["route"] end |
#route=(value) ⇒ Object
228 229 230 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 228 def route=(value) @page_props["route"] = value end |
#save_file(dialog_title: nil, file_name: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, src_bytes: nil, timeout: nil, on_result: nil) ⇒ Object
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 926 def save_file( dialog_title: nil, file_name: nil, initial_directory: nil, file_type: "any", allowed_extensions: nil, src_bytes: nil, timeout: nil, on_result: nil ) invoke_file_picker( "save_file", compact_service_args( "dialog_title" => dialog_title, "file_name" => file_name, "initial_directory" => initial_directory, "file_type" => file_type, "allowed_extensions" => allowed_extensions, "src_bytes" => src_bytes ), timeout: timeout, on_result: on_result ) end |
#schedule_update ⇒ Object
1330 1331 1332 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1330 def schedule_update update end |
#screen_brightness(**props) ⇒ Object
436 437 438 439 440 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 436 def screen_brightness(**props) return service(:screen_brightness, **props) unless props.empty? @screen_brightness_proxy end |
#screenshot(**props) ⇒ Object
1095 1096 1097 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1095 def screenshot(**props) Ruflet::UI::ControlFactory.build(:screenshot, **props) end |
#scroll_to(offset: nil, delta: nil, scroll_key: nil, duration: nil, curve: nil, timeout: 10, on_result: nil) ⇒ Object
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 798 def scroll_to(offset: nil, delta: nil, scroll_key: nil, duration: nil, curve: nil, timeout: 10, on_result: nil) invoke( :page, "scroll_to", args: { "offset" => offset, "delta" => delta, "scroll_key" => scroll_key, "duration" => duration, "curve" => curve }, timeout: timeout, on_result: on_result ) end |
#secure_storage(**props) ⇒ Object
1083 1084 1085 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1083 def secure_storage(**props) service(:secure_storage, **props) end |
#selection_click(timeout: 10, on_result: nil) ⇒ Object
988 989 990 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 988 def selection_click(timeout: 10, on_result: nil) invoke_haptic_feedback("selection_click", timeout: timeout, on_result: on_result) end |
#semantics_service(**props) ⇒ Object
1091 1092 1093 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1091 def semantics_service(**props) service(:semantics_service, **props) end |
#service(type, **props) ⇒ Object
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 478 def service(type, **props) mapped_props = normalize_props(props || {}) id = mapped_props.delete("id") normalized_type = type.to_s.downcase compact_type = normalized_type.delete("_") if visual_service_type?(normalized_type) key = id ? "id:#{id}" : normalized_type existing = @visual_service_controls[key] return existing if existing svc = Ruflet::UI::ControlFactory.build(type.to_s, id: id&.to_s, **mapped_props) @visual_service_controls[key] = svc return svc end unless service_control_type?(normalized_type) raise ArgumentError, "#{type} is a visual control, not a page service" end existing = if id services.find { |s| s.is_a?(Control) && s.id.to_s == id.to_s } else services.find do |s| s.is_a?(Control) && s.type.to_s.downcase.delete("_") == compact_type end end if existing unless mapped_props.empty? existing.merge_props(mapped_props) refresh_services_container! push_services_update! end return existing end svc = Ruflet::UI::ControlFactory.build(type.to_s, id: id&.to_s, **mapped_props) add_service(svc) unless services.include?(svc) svc end |
#services ⇒ Object
407 408 409 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 407 def services @services_container.props["_services"] ||= [] end |
#services=(value) ⇒ Object
411 412 413 414 415 416 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 411 def services=(value) @services_container.props["_services"] = Array(value).compact refresh_services_container! push_services_update! self end |
#set_clipboard(value, timeout: nil, on_result: nil) ⇒ Object
996 997 998 999 1000 1001 1002 1003 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 996 def set_clipboard(value, timeout: nil, on_result: nil) invoke_clipboard_method( "set", args: { "data" => value.to_s }, timeout: timeout, on_result: on_result ) end |
#set_clipboard_files(files, timeout: nil, on_result: nil) ⇒ Object
1009 1010 1011 1012 1013 1014 1015 1016 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1009 def set_clipboard_files(files, timeout: nil, on_result: nil) invoke_clipboard_method( "set_files", args: { "files" => Array(files).map(&:to_s) }, timeout: timeout, on_result: on_result ) end |
#set_clipboard_image(value, timeout: nil, on_result: nil) ⇒ Object
1022 1023 1024 1025 1026 1027 1028 1029 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1022 def set_clipboard_image(value, timeout: nil, on_result: nil) invoke_clipboard_method( "set_image", args: { "data" => value }, timeout: timeout, on_result: on_result ) end |
#set_view_props(props) ⇒ Object
211 212 213 214 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 211 def set_view_props(props) split_props(normalize_props(props || {})) self end |
#shake_detector(**props) ⇒ Object
1087 1088 1089 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1087 def shake_detector(**props) service(:shake_detector, **props) end |
#share(**props) ⇒ Object
1123 1124 1125 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1123 def share(**props) service(:share, **props) end |
#share_files(files = nil, text: nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1227 def share_files( files = nil, text: nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil ) share = ensure_share_service invoke( share, "share_files", args: compact_service_args( "files" => normalize_share_files(files), "text" => text, "title" => title, "subject" => subject, "preview_thumbnail" => normalize_share_file(preview_thumbnail), "share_position_origin" => share_position_origin, "download_fallback_enabled" => download_fallback_enabled, "mail_to_fallback_enabled" => mail_to_fallback_enabled, "excluded_cupertino_activities" => excluded_cupertino_activities ), timeout: timeout, on_result: on_result ) end |
#share_text(text = nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1175 def share_text( text = nil, title: nil, subject: nil, preview_thumbnail: nil, share_position_origin: nil, download_fallback_enabled: true, mail_to_fallback_enabled: true, excluded_cupertino_activities: nil, timeout: nil, on_result: nil ) share = ensure_share_service invoke( share, "share_text", args: compact_service_args( "text" => text, "title" => title, "subject" => subject, "preview_thumbnail" => preview_thumbnail, "share_position_origin" => share_position_origin, "download_fallback_enabled" => download_fallback_enabled, "mail_to_fallback_enabled" => mail_to_fallback_enabled, "excluded_cupertino_activities" => excluded_cupertino_activities ), timeout: timeout, on_result: on_result ) end |
#share_uri(uri = nil, share_position_origin: nil, excluded_cupertino_activities: nil, timeout: nil, on_result: nil) ⇒ Object
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1206 def share_uri( uri = nil, share_position_origin: nil, excluded_cupertino_activities: nil, timeout: nil, on_result: nil ) share = ensure_share_service invoke( share, "share_uri", args: compact_service_args( "uri" => uri, "share_position_origin" => share_position_origin, "excluded_cupertino_activities" => excluded_cupertino_activities ), timeout: timeout, on_result: on_result ) end |
#shared_preferences(**props) ⇒ Object
418 419 420 421 422 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 418 def shared_preferences(**props) return service(:shared_preferences, **props) unless props.empty? @shared_preferences_proxy end |
#show_banner(banner_control) ⇒ Object
746 747 748 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 746 def () show_dialog() end |
#show_bottom_sheet(bottom_sheet_control) ⇒ Object
729 730 731 732 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 729 def show_bottom_sheet(bottom_sheet_control) @bottom_sheet = bottom_sheet_control show_dialog(bottom_sheet_control) end |
#show_bottomsheet(bottom_sheet_control) ⇒ Object
734 735 736 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 734 def show_bottomsheet(bottom_sheet_control) show_bottom_sheet(bottom_sheet_control) end |
#show_dialog(dialog_control) ⇒ Object
708 709 710 711 712 713 714 715 716 717 718 719 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 708 def show_dialog(dialog_control) return self unless dialog_control return self if dialog_open?(dialog_control) dialog_control.props["open"] = true @dialogs << dialog_control unless @dialogs.include?(dialog_control) refresh_dialogs_container! send_view_patch unless @dialogs_container.wire_id push_dialogs_update! self end |
#show_drawer(timeout: 10, on_result: nil) ⇒ Object
778 779 780 781 782 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 778 def show_drawer(timeout: 10, on_result: nil) raise ArgumentError, "No drawer defined" unless drawer invoke(:page, "show_drawer", timeout: timeout, on_result: on_result) end |
#show_end_drawer(timeout: 10, on_result: nil) ⇒ Object
788 789 790 791 792 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 788 def show_end_drawer(timeout: 10, on_result: nil) raise ArgumentError, "No end_drawer defined" unless end_drawer invoke(:page, "show_end_drawer", timeout: timeout, on_result: on_result) end |
#show_snack_bar(snack_bar_control) ⇒ Object
721 722 723 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 721 def () show_dialog() end |
#show_snackbar(snack_bar_control) ⇒ Object
725 726 727 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 725 def () show_dialog() end |
#snack_bar ⇒ Object
686 687 688 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 686 def @snack_bar end |
#snack_bar=(value) ⇒ Object
680 681 682 683 684 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 680 def (value) @snack_bar = value refresh_dialogs_container! push_dialogs_update! if @dialogs_container_mounted end |
#snackbar=(value) ⇒ Object
690 691 692 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 690 def (value) self. = value end |
#spacing ⇒ Object
648 649 650 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 648 def spacing @view_props["spacing"] end |
#spacing=(value) ⇒ Object
652 653 654 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 652 def spacing=(value) @view_props["spacing"] = value end |
#storage_paths(**props) ⇒ Object
1119 1120 1121 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1119 def storage_paths(**props) service(:storage_paths, **props) end |
#supports_close_for_launch_mode(mode, timeout: 10, on_result: nil) ⇒ Object
895 896 897 898 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 895 def supports_close_for_launch_mode(mode, timeout: 10, on_result: nil) url_launcher = ensure_url_launcher_service invoke(url_launcher, "supports_close_for_launch_mode", args: { "mode" => mode }, timeout: timeout, on_result: on_result) end |
#supports_launch_mode(mode, timeout: 10, on_result: nil) ⇒ Object
890 891 892 893 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 890 def supports_launch_mode(mode, timeout: 10, on_result: nil) url_launcher = ensure_url_launcher_service invoke(url_launcher, "supports_launch_mode", args: { "mode" => mode }, timeout: timeout, on_result: on_result) end |
#title ⇒ Object
216 217 218 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 216 def title @page_props["title"] end |
#title=(value) ⇒ Object
220 221 222 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 220 def title=(value) @page_props["title"] = value end |
#update(control_or_id = nil, **props) ⇒ Object
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1282 def update(control_or_id = nil, **props) if control_or_id.nil? && props.empty? send_view_patch return self end if page_control_target?(control_or_id) split_props(normalize_props(props)) send_view_patch return self end control = resolve_control(control_or_id) return self unless control wire_id = control.wire_id if wire_id.nil? # Events can race with navigation/disposal; never emit patch_control with nil id. refresh_control_indexes! wire_id = control.wire_id end return self if wire_id.nil? patch = normalize_props(props) if text_maps_to_content?(control, patch) patch["content"] = patch.delete("text") end patch.each { |key, value| control.props[key] = value } # Keep runtime control tree aligned with incremental patches. if patch.key?("controls") control.children.clear Array(patch["controls"]).each { |child| control.children << child if child.is_a?(Control) } end visited = Set.new patch.each_value { |value| (value, visited) } patch_ops = patch.map { |k, v| [0, 0, k, serialize_patch_value(v)] } (Protocol::ACTIONS[:patch_control], { "id" => wire_id, "patch" => [[0], *patch_ops] }) self end |
#upload(files, timeout: nil, on_result: nil) ⇒ Object
963 964 965 966 967 968 969 970 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 963 def upload(files, timeout: nil, on_result: nil) invoke_file_picker( "upload", { "files" => Array(files).map { |file| normalize_service_value(file) } }, timeout: timeout, on_result: on_result ) end |
#upload_files(files, timeout: nil, on_result: nil) ⇒ Object
972 973 974 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 972 def upload_files(files, timeout: nil, on_result: nil) upload(files, timeout: timeout, on_result: on_result) end |
#url_launcher(**props) ⇒ Object
1115 1116 1117 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1115 def url_launcher(**props) service(:url_launcher, **props) end |
#user_accelerometer(**props) ⇒ Object
1063 1064 1065 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 1063 def user_accelerometer(**props) service(:user_accelerometer, **props) end |
#vertical_alignment ⇒ Object
232 233 234 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 232 def vertical_alignment @page_props["vertical_alignment"] || @view_props["vertical_alignment"] end |
#vertical_alignment=(value) ⇒ Object
236 237 238 239 240 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 236 def vertical_alignment=(value) v = normalize_value("vertical_alignment", value) @page_props["vertical_alignment"] = v @view_props["vertical_alignment"] = v end |
#vibrate(timeout: 10, on_result: nil) ⇒ Object
992 993 994 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 992 def vibrate(timeout: 10, on_result: nil) invoke_haptic_feedback("vibrate", timeout: timeout, on_result: on_result) end |
#wakelock(**props) ⇒ Object
424 425 426 427 428 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 424 def wakelock(**props) return service(:wakelock, **props) unless props.empty? @wakelock_proxy end |
#web ⇒ Object Also known as: web?
Older and embedded clients may omit this capability from registration.
Ruflet applications should still be able to branch on page.web just as
they do with a full desktop client.
21 22 23 |
# File 'lib/ruflet_ui/ruflet/page.rb', line 21 def web !!(@client_details && @client_details["web"]) end |