-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: add Niente, a 'do nothing' display service
- Loading branch information
Showing
7 changed files
with
285 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
# Niente -- Italian for "nothing" -- is a null display service | ||
# that doesn't display anything. It does very little, while | ||
# keeping a certain amount of "mental model" or schema of | ||
# how a real display service should act. | ||
module Niente; end | ||
|
||
require_relative "niente/logger" | ||
Shoes::Log.instance = Niente::LogImpl.new | ||
|
||
require_relative "niente/drawable" | ||
require_relative "niente/app" | ||
require_relative "niente/display_service" | ||
|
||
if ENV["SHOES_SPEC_TEST"] | ||
require_relative "niente/shoes_spec" | ||
Shoes::Spec.instance = Niente::Test | ||
end | ||
|
||
Shoes::DisplayService.set_display_service_class(Niente::DisplayService) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# frozen_string_literal: true | ||
|
||
module Niente | ||
class App < Drawable | ||
def initialize(properties) | ||
super | ||
|
||
bind_shoes_event(event_name: "init") { init } | ||
bind_shoes_event(event_name: "run") { run } | ||
bind_shoes_event(event_name: "destroy") { destroy } | ||
end | ||
|
||
def init | ||
end | ||
|
||
def run | ||
send_shoes_event("wait", event_name: "custom_event_loop") | ||
end | ||
|
||
def destroy | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# frozen_string_literal: true | ||
|
||
module Niente | ||
# This is a "null" DisplayService, doing as little as it | ||
# can get away with. | ||
class DisplayService < Shoes::DisplayService | ||
include Shoes::Log | ||
|
||
class << self | ||
attr_accessor :instance | ||
end | ||
|
||
def initialize | ||
if Niente::DisplayService.instance | ||
raise Scarpe::SingletonError, "ERROR! This is meant to be a singleton!" | ||
end | ||
|
||
Niente::DisplayService.instance = self | ||
|
||
log_init("Niente::DisplayService") | ||
super() | ||
end | ||
|
||
# Create a fake display drawable for a specific Shoes drawable, and pair it with | ||
# the linkable ID for this Shoes drawable. | ||
# | ||
# @param drawable_class_name [String] The class name of the Shoes drawable, e.g. Shoes::Button | ||
# @param drawable_id [String] the linkable ID for drawable events | ||
# @param properties [Hash] a JSON-serialisable Hash with the drawable's Shoes styles | ||
# @param is_widget [Boolean] whether the class is a user-defined Shoes::Widget subclass | ||
# @return [Webview::Drawable] the newly-created Webview drawable | ||
def create_display_drawable_for(drawable_class_name, drawable_id, properties, is_widget:) | ||
existing = query_display_drawable_for(drawable_id, nil_ok: true) | ||
if existing | ||
@log.warn("There is already a display drawable for #{drawable_id.inspect}! Returning #{existing.class.name}.") | ||
return existing | ||
end | ||
|
||
if drawable_class_name == "App" | ||
@app = Niente::App.new(properties) | ||
set_drawable_pairing(drawable_id, @app) | ||
|
||
return @app | ||
end | ||
|
||
display_drawable = Niente::Drawable.new(properties) | ||
set_drawable_pairing(drawable_id, display_drawable) | ||
|
||
return display_drawable | ||
end | ||
|
||
# Destroy the display service and the app. Quit the process (eventually.) | ||
# | ||
# @return [void] | ||
def destroy | ||
@app.destroy | ||
DisplayService.instance = nil | ||
end | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
module Niente | ||
class Drawable < Shoes::Linkable | ||
attr_reader :shoes_linkable_id | ||
attr_reader :parent | ||
attr_reader :children | ||
|
||
def initialize(props) | ||
@shoes_linkable_id = props.delete("shoes_linkable_id") || props.delete(:shoes_linkable_id) | ||
@data = props | ||
|
||
super(linkable_id: @shoes_linkable_id) | ||
|
||
bind_shoes_event(event_name: "parent", target: shoes_linkable_id) do |new_parent_id| | ||
display_parent = DisplayService.instance.query_display_drawable_for(new_parent_id) | ||
if @parent != display_parent | ||
set_parent(display_parent) | ||
end | ||
end | ||
|
||
# When Shoes drawables change properties, we get a change notification here | ||
bind_shoes_event(event_name: "prop_change", target: shoes_linkable_id) do |prop_changes| | ||
prop_changes.each do |k, v| | ||
instance_variable_set("@" + k, v) | ||
end | ||
properties_changed(prop_changes) if respond_to?(:properties_changed) | ||
end | ||
|
||
bind_shoes_event(event_name: "destroy", target: shoes_linkable_id) do | ||
end | ||
end | ||
|
||
def set_parent(new_parent) | ||
@parent&.remove_child(self) | ||
new_parent&.add_child(self) | ||
@parent = new_parent | ||
end | ||
|
||
# Do not call directly, use set_parent | ||
def remove_child(child) | ||
@children ||= [] | ||
unless @children.include?(child) | ||
STDERR.puts("remove_child: no such child(#{child.inspect}) for"\ | ||
" parent(#{parent.inspect})!") | ||
end | ||
@children.delete(child) | ||
end | ||
|
||
# Do not call directly, use set_parent | ||
def add_child(child) | ||
@children ||= [] | ||
@children << child | ||
end | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# frozen_string_literal: true | ||
|
||
require "shoes/log" | ||
require "json" | ||
|
||
module Niente; end | ||
class Niente::LogImpl | ||
include Shoes::Log # for constants | ||
|
||
class PrintLogger | ||
def initialize(_) | ||
end | ||
|
||
[:error, :warn, :debug, :info].each do |level| | ||
define_method(level) do |msg| | ||
puts "#{level}: #{msg}" | ||
end | ||
end | ||
end | ||
|
||
def logger_for_component(component) | ||
PrintLogger.new(component.to_s) | ||
end | ||
|
||
def configure_logger(log_config) | ||
end | ||
end | ||
|
||
#Shoes::Log.instance = Niente::LogImpl.new |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# frozen_string_literal: true | ||
|
||
require "minitest" | ||
require "scarpe/components/string_helpers" | ||
|
||
module Niente; end | ||
|
||
class Niente::Test | ||
def self.run_shoes_spec_test_code(code, class_name: nil, test_name: nil) | ||
if @shoes_spec_init | ||
raise MultipleShoesSpecRunsError, "Scarpe-Webview can only run a single Shoes spec per process!" | ||
end | ||
@shoes_spec_init = true | ||
|
||
require "scarpe/components/minitest_export_reporter" | ||
Minitest::Reporters::ShoesExportReporter.activate! | ||
|
||
class_name ||= ENV["SHOES_MINITEST_CLASS_NAME"] || "TestShoesSpecCode" | ||
test_name ||= ENV["SHOES_MINITEST_METHOD_NAME"] || "test_shoes_spec" | ||
|
||
Shoes::DisplayService.subscribe_to_event("heartbeat", nil) do | ||
unless @hb_init | ||
Minitest.run [] | ||
Shoes::App.instance.destroy | ||
end | ||
@hb_init = true | ||
end | ||
|
||
test_class = Class.new(Niente::ShoesSpecTest) | ||
Object.const_set(Scarpe::Components::StringHelpers.camelize(class_name), test_class) | ||
test_name = "test_" + test_name unless test_name.start_with?("test_") | ||
test_class.define_method(test_name) do | ||
STDERR.puts "Started running #{class_name.inspect}::#{test_name.inspect}" | ||
eval(code) | ||
end | ||
end | ||
end | ||
|
||
class Niente::ShoesSpecTest < Minitest::Test | ||
Shoes::Drawable.drawable_classes.each do |drawable_class| | ||
finder_name = drawable_class.dsl_name | ||
|
||
define_method(finder_name) do |*args| | ||
app = Shoes::App.instance | ||
|
||
drawables = app.find_drawables_by(drawable_class, *args) | ||
raise Scarpe::MultipleDrawablesFoundError, "Found more than one #{finder_name} matching #{args.inspect}!" if drawables.size > 1 | ||
raise Scarpe::NoDrawablesFoundError, "Found no #{finder_name} matching #{args.inspect}!" if drawables.empty? | ||
|
||
Niente::ShoesSpecProxy.new(drawables[0]) | ||
end | ||
end | ||
end | ||
|
||
class Niente::ShoesSpecProxy | ||
attr_reader :obj | ||
attr_reader :linkable_id | ||
attr_reader :display | ||
|
||
JS_EVENTS = [:click, :hover, :leave, :change] | ||
|
||
def initialize(obj) | ||
@obj = obj | ||
@linkable_id = obj.linkable_id | ||
@display = ::Shoes::DisplayService.display_service.query_display_drawable_for(obj.linkable_id) | ||
end | ||
|
||
def method_missing(method, ...) | ||
if @obj.respond_to?(method) | ||
self.singleton_class.define_method(method) do |*args, **kwargs, &block| | ||
@obj.send(method, *args, **kwargs, &block) | ||
end | ||
send(method, ...) | ||
else | ||
super # raise an exception | ||
end | ||
end | ||
|
||
def trigger(event_name, *args) | ||
# TODO | ||
end | ||
|
||
JS_EVENTS.each do |ev| | ||
define_method "trigger_#{ev}" do |*args| | ||
trigger(ev, *args) | ||
end | ||
end | ||
|
||
def respond_to_missing?(method_name, include_private = false) | ||
@obj.respond_to_missing?(method_name, include_private) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters