Class: Shoes::Drawable

Inherits:
Linkable show all
Includes:
MarginHelper, Colors, Log
Defined in:
lacci/lib/shoes.rb,
lacci/lib/shoes/download.rb,
lacci/lib/shoes/drawable.rb,
lacci/lib/shoes/drawables/para.rb

Overview

Shoes::Drawable

This is the display-service portable Shoes Drawable interface. Visible Shoes drawables like buttons inherit from this. Compound drawables made of multiple different smaller Drawables inherit from it in their various apps or libraries. The Shoes Drawable helps build a Shoes-side drawable tree, with parents and children. Any API that applies to all drawables (e.g. remove) should be defined here.

Defined Under Namespace

Classes: ResponseWrapper

Constant Summary collapse

DRAW_CONTEXT_STYLES =

These styles can be set to a current per-slot value and inherited from parent slots. Their value is set at drawable-create time.

[:fill, :stroke, :strokewidth, :rotate, :transform, :translate]

Constants included from Log

Log::DEFAULT_COMPONENT, Log::DEFAULT_DEBUG_LOG_CONFIG, Log::DEFAULT_LOG_CONFIG

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes inherited from Linkable

#linkable_id

Class Method Summary collapse

Instance Method Summary collapse

Methods included from MarginHelper

#margin_parse

Methods included from Colors

#gray, #rgb, #to_rgb

Methods included from Log

configure_logger, #log_init, logger

Methods inherited from Linkable

#bind_shoes_event, #send_self_event, #send_shoes_event, #unsub_all_shoes_events, #unsub_shoes_event

Constructor Details

#initialize(*args, **kwargs) ⇒ Drawable

Returns a new instance of Drawable.



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lacci/lib/shoes/drawable.rb', line 279

def initialize(*args, **kwargs)
  kwargs = margin_parse(kwargs)
  log_init("Shoes::#{self.class.name}") unless @log

  # Grab the current app, mark it as used
  @app = self.is_a?(Shoes::App) ? self : Drawable.use_current_app

  # First, get the list of allowed and disallowed styles for the given features
  # and make sure no disallowed styles were given.

  app_features = @app.features
  this_app_styles = self.class.shoes_style_names(with_features: @app.features).map(&:to_sym)
  not_this_app_styles = self.class.shoes_style_names(with_features: :all).map(&:to_sym) - this_app_styles

  bad_styles = kwargs.keys & not_this_app_styles
  unless bad_styles.empty?
    features_needed = bad_styles.map { |s| self.class.feature_for_shoes_style(s) }.uniq
    raise Shoes::Errors::UnsupportedFeatureError, "The style(s) #{bad_styles.inspect} are only defined for applications that request specific features: #{features_needed.inspect} (you requested #{app_features.inspect})!"
  end

  # Next, check positional arguments and make sure the correct number and type
  # were passed and match positional args with style names.

  supplied_args = kwargs.keys

  req_args = self.class.required_init_args
  opt_args = self.class.optional_init_args
  pos_args = req_args + opt_args
  if req_args != ["any"]
    if args.size > pos_args.size
      raise Shoes::Errors::BadArgumentListError, "Too many arguments given for #{self.class}#initialize! #{args.inspect}"
    end

    if args.size == 0
      # It's fine to use keyword args instead, but we should make sure they're actually there
      needed_args = req_args.map(&:to_sym) - kwargs.keys
      unless needed_args.empty?
        raise Shoes::Errors::BadArgumentListError, "Keyword arguments for #{self.class}#initialize should also supply #{needed_args.inspect}! #{args.inspect}"
      end
    elsif args.size < req_args.size
      raise Shoes::Errors::BadArgumentListError, "Too few arguments given for #{self.class}#initialize! #{args.inspect}"
    end

    # Set each positional argument
    args.each.with_index do |val, idx|
      style_name = pos_args[idx]
      next if style_name.nil? || style_name == "" # It's possible to have non-style positional args

      val = self.class.validate_as(style_name, args[idx])
      instance_variable_set("@#{style_name}", val)
      supplied_args << style_name.to_sym
    end
  end

  this_drawable_styles = self.class.shoes_style_names(with_features: @app.features).map(&:to_sym)
  dc = @app.current_draw_context || {}

  # Styles not passed as arguments can come from the draw context

  # What styles are in the draw context, are used by this drawable, and weren't
  # given as positional or keyword arguments?
  draw_context_styles = (DRAW_CONTEXT_STYLES & this_drawable_styles) - supplied_args
  unless draw_context_styles.empty?
    draw_context_styles.each do |style|
      dc_val = dc[style.to_s]
      next if dc_val.nil?

      val = self.class.validate_as(style, dc[style.to_s])
      instance_variable_set("@#{style}", val)
      supplied_args << style
    end
  end

  # Styles that were *not* passed should be set to defaults

  default_styles = Shoes::Drawable.drawable_default_styles[self.class]

  # No arg specified for a property with a default value? Set it to default.
  (default_styles.keys - supplied_args).each do |key|
    val = self.class.validate_as(key, default_styles[key])
    instance_variable_set("@#{key}", val)
  end

  # If we have a keyword arg for a style, set it as specified.
  (this_drawable_styles & kwargs.keys).each do |key|
    val = self.class.validate_as(key, kwargs[key])
    instance_variable_set("@#{key}", val)
  end

  # We'd like to avoid unexpected keywords. But we're not disciplined enough to
  # raise an error by default yet. Non-style keywords passed to Drawable#initialize
  # are deprecated at this point, but I need to hunt down the last of them
  # and prevent them.
  unexpected = (kwargs.keys - this_drawable_styles)
  unless unexpected.empty?
    STDERR.puts "Unexpected non-style keyword(s) in #{self.class} initialize: #{unexpected.inspect}"
  end

  super(linkable_id: Shoes::Drawable.allocate_drawable_id)
  Shoes::Drawable.register_drawable_id(self.linkable_id, self)

  generate_debug_id

  parent = @app.current_slot
  if self.class.expects_parent?
    set_parent(parent, notify: false)
  end

  unless self.class.registered_shoes_events?
    # No Shoes events declared and we're creating an instance?
    # Default to no class-specific events.
    self.class.shoes_events
  end

  # Binding the motion events here isn't perfect.
  # What about drawables like SubscriptionItem that
  # have no motion events? With the current Lacci
  # implementation, the answer is that those events
  # will never be sent. Calling .hover on one will
  # be useless, harmless, and allowed. If you want
  # to make it disallowed, you can do something like
  # define a SubscriptionItem#hover that raises an
  # exception instead.

  bind_self_event("hover") do
    @hover&.call
  end

  bind_self_event("leave") do
    @leave&.call
  end

  bind_self_event("motion") do |x, y|
    @motion&.call(x, y)
  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 to auto-create Shoes style getters and setters.



606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lacci/lib/shoes/drawable.rb', line 606

def method_missing(name, *args, **kwargs, &block)
  name_s = name.to_s

  if name_s[-1] == "="
    prop_name = name_s[0..-2]
    if self.class.shoes_style_name?(prop_name)
      self.class.define_method(name) do |new_value|
        raise(Shoes::Errors::NoSuchLinkableIdError, "Trying to set Shoes styles in a #{self.class} with no linkable ID!") unless linkable_id

        new_value = self.class.validate_as(prop_name, new_value)
        instance_variable_set("@" + prop_name, new_value)
        send_shoes_event({ prop_name => new_value }, event_name: "prop_change", target: linkable_id)
      end

      return self.send(name, *args, **kwargs, &block)
    end
  end

  if self.class.shoes_style_name?(name_s)
    self.class.define_method(name) do
      raise(Shoes::Errors::NoSuchLinkableIdError, "Trying to get Shoes styles in an object with no linkable ID! #{inspect}") unless linkable_id

      instance_variable_get("@" + name_s)
    end

    return self.send(name, *args, **kwargs, &block)
  end

  super(name, *args, **kwargs, &block)
end

Class Attribute Details

.drawable_classesObject

Returns the value of attribute drawable_classes.



21
22
23
# File 'lacci/lib/shoes/drawable.rb', line 21

def drawable_classes
  @drawable_classes
end

.drawable_default_stylesObject

Returns the value of attribute drawable_default_styles.



22
23
24
# File 'lacci/lib/shoes/drawable.rb', line 22

def drawable_default_styles
  @drawable_default_styles
end

.widget_classesObject

Returns the value of attribute widget_classes.



23
24
25
# File 'lacci/lib/shoes/drawable.rb', line 23

def widget_classes
  @widget_classes
end

Instance Attribute Details

#debug_idObject (readonly)

Returns the value of attribute debug_id.



271
272
273
# File 'lacci/lib/shoes/drawable.rb', line 271

def debug_id
  @debug_id
end

#destroyedObject (readonly)

Returns the value of attribute destroyed.



542
543
544
# File 'lacci/lib/shoes/drawable.rb', line 542

def destroyed
  @destroyed
end

#parentObject (readonly)

Returns the value of attribute parent.



541
542
543
# File 'lacci/lib/shoes/drawable.rb', line 541

def parent
  @parent
end

Class Method Details

.allocate_drawable_idObject

Assign a new Shoes Drawable ID number, starting from 1. This allows non-overlapping small integer IDs for Shoes linkable IDs - the number part of making it clear what widget you're talking about.



137
138
139
140
141
# File 'lacci/lib/shoes/drawable.rb', line 137

def allocate_drawable_id
  @drawable_id_counter ||= 0
  @drawable_id_counter += 1
  @drawable_id_counter
end

.convert_to_float(value, attribute_name) ⇒ Object



658
659
660
661
662
663
664
665
666
667
668
# File 'lacci/lib/shoes/drawable.rb', line 658

def self.convert_to_float(value, attribute_name)
  begin
    value = Float(value)
    raise Shoes::Errors::InvalidAttributeValueError, "Negative number '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0

    value
  rescue ArgumentError
    error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
    raise Shoes::Errors::InvalidAttributeValueError, error_message
  end
end

.convert_to_integer(value, attribute_name) ⇒ Object



646
647
648
649
650
651
652
653
654
655
656
# File 'lacci/lib/shoes/drawable.rb', line 646

def self.convert_to_integer(value, attribute_name)
  begin
    value = Integer(value)
    raise Shoes::Errors::InvalidAttributeValueError, "Negative number '#{value}' not allowed for attribute '#{attribute_name}'" if value < 0

    value
  rescue ArgumentError
    error_message = "Invalid value '#{value}' provided for attribute '#{attribute_name}'. The value should be a number."
    raise Shoes::Errors::InvalidAttributeValueError, error_message
  end
end

.drawable_by_id(id, none_ok: false) ⇒ Object



153
154
155
156
157
158
159
160
# File 'lacci/lib/shoes/drawable.rb', line 153

def drawable_by_id(id, none_ok: false)
  val = @drawables_by_id[id]
  unless val || none_ok
    raise "No Drawable Found! #{@drawables_by_id.inspect}"
  end

  val
end

.drawable_class_by_name(name) ⇒ Object



45
46
47
48
# File 'lacci/lib/shoes/drawable.rb', line 45

def drawable_class_by_name(name)
  name = name.to_s
  drawable_classes.detect { |k| k.dsl_name == name }
end

.dsl_nameObject



40
41
42
43
# File 'lacci/lib/shoes/drawable.rb', line 40

def dsl_name
  n = name.split("::").last.chomp("Drawable")
  n.gsub(/(.)([A-Z])/, '\1_\2').downcase
end

.expects_parent?Boolean

Returns:

  • (Boolean)


416
417
418
419
420
421
# File 'lacci/lib/shoes/drawable.rb', line 416

def self.expects_parent?
  return false if [::Shoes::App, ::Shoes::DocumentRoot].include?(self)
  return false if self < ::Shoes::TextDrawable

  true
end

.feature_for_shoes_style(style_name) ⇒ Object

Query what feature, if any, is required to use a specific shoes_style. If no specific feature is needed, nil will be returned.



200
201
202
203
204
205
206
207
208
209
210
211
# File 'lacci/lib/shoes/drawable.rb', line 200

def feature_for_shoes_style(style_name)
  style_name = style_name.to_s
  lp = linkable_properties.detect { |prop| prop[:name] == style_name }
  return lp[:feature] if lp

  # If we get to the top of the superclass tree and we didn't find it, it's not here
  if self.class == ::Shoes::Drawable
    raise Shoes::Errors::NoSuchStyleError, "Can't find information for style #{style_name.inspect}!"
  end

  super
end

.get_shoes_eventsObject

Return a list of Shoes events for this class.



70
71
72
73
74
75
76
# File 'lacci/lib/shoes/drawable.rb', line 70

def get_shoes_events
  if @shoes_events.nil?
    raise Shoes::Errors::UnknownEventsForClassError, "Drawable type #{self} hasn't defined its list of Shoes events!"
  end

  @shoes_events
end

.init_args(*args) ⇒ void

This method returns an undefined value.

Require supplying these Shoes style values as positional arguments to initialize. Initialize will get the arg list, then set the specified styles if args are given for them. @see opt_init_args for additional non-required init args.

Parameters:

  • args (Array<String,Symbol>)

    an array of Shoes style names

Raises:



103
104
105
106
# File 'lacci/lib/shoes/drawable.rb', line 103

def init_args(*args)
  raise Shoes::Errors::BadArgumentListError, "Positional init args already set for #{self}!" if @required_init_args
  @required_init_args = args.map(&:to_s)
end

.is_widget_class?(name) ⇒ Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lacci/lib/shoes/drawable.rb', line 50

def is_widget_class?(name)
  !!Shoes::Drawable.widget_classes.intersect?([name.to_s])
end

.opt_init_args(*args) ⇒ void

This method returns an undefined value.

Allow supplying these Shoes style values as optional positional arguments to initialize after the mandatory args. @see init_args for setting required init args.

Parameters:

  • args (Array<String,Symbol>)

    an array of Shoes style names

Raises:



114
115
116
117
# File 'lacci/lib/shoes/drawable.rb', line 114

def opt_init_args(*args)
  raise Shoes::Errors::BadArgumentListError, "Positional init args already set for #{self}!" if @opt_init_args
  @opt_init_args = args.map(&:to_s)
end

.optional_init_argsArray<String>

Return the list of style names for optional init args for this class

Returns:

  • (Array<String>)

    the array of style names as strings



129
130
131
# File 'lacci/lib/shoes/drawable.rb', line 129

def optional_init_args
  @opt_init_args ||= []
end

.register_drawable_id(id, drawable) ⇒ Object



143
144
145
146
# File 'lacci/lib/shoes/drawable.rb', line 143

def register_drawable_id(id, drawable)
  @drawables_by_id ||= {}
  @drawables_by_id[id] = drawable
end

.registered_shoes_events?Boolean

Return whether Shoes events have already been registered for this class

Returns:

  • (Boolean)

    true if events have been registered, false if not



81
82
83
# File 'lacci/lib/shoes/drawable.rb', line 81

def registered_shoes_events?
  !@shoes_events.nil?
end

.required_init_argsArray<String>

Return the list of style names for required init args for this class

Returns:

  • (Array<String>)

    the array of style names as strings



122
123
124
# File 'lacci/lib/shoes/drawable.rb', line 122

def required_init_args
  @required_init_args ||= [] # TODO: eventually remove the ||= here
end

.shoes_events(*args) ⇒ void

This method returns an undefined value.

Set the list of Shoes event names that are allowed for this class.

Parameters:

  • args (Array)

    an array of event names, which will be coerced to Strings



89
90
91
92
93
94
# File 'lacci/lib/shoes/drawable.rb', line 89

def shoes_events(*args)
  if @shoes_events
    raise Shoes::Errors::DoubleRegisteredShoesEventError, "Registering shoes events #{args.inspect} for class #{self} but already registered events as #{@shoes_events.inspect}!"
  end
  @shoes_events = args.map(&:to_s) + self.superclass.get_shoes_events
end

.shoes_style(name, feature: nil, &validator) ⇒ Object

Shoes styles in Shoes Linkables are automatically sync'd with the display side objects. If a block is passed to shoes_style, that's the validation for the property. It should convert a given value to a valid value for the property or throw an exception.

If feature is non-nil, it's the feature that an app must request in order to see this property.

Parameters:

  • name (String, Symbol)

    the style name

  • feature (Symbol, NilClass) (defaults to: nil)

    the feature that must be defined for an app to request this style, or nil



184
185
186
187
188
189
190
191
# File 'lacci/lib/shoes/drawable.rb', line 184

def shoes_style(name, feature: nil, &validator)
  name = name.to_s

  return if linkable_properties_hash[name]

  linkable_properties << { name: name, validator:, feature: }
  linkable_properties_hash[name] = true
end

.shoes_style_hashesObject



229
230
231
232
233
# File 'lacci/lib/shoes/drawable.rb', line 229

def shoes_style_hashes
  parent_hashes = self != Shoes::Drawable ? self.superclass.shoes_style_hashes : []

  parent_hashes + linkable_properties
end

.shoes_style_name?(name) ⇒ Boolean

Returns:

  • (Boolean)


235
236
237
238
# File 'lacci/lib/shoes/drawable.rb', line 235

def shoes_style_name?(name)
  linkable_properties_hash[name.to_s] ||
    (self != Shoes::Drawable && superclass.shoes_style_name?(name))
end

.shoes_style_names(with_features: nil) ⇒ Object

Return a list of shoes_style names with the given features. If with_features is nil, return them with a list of features for the current Shoes::App. For the list of styles available with no features requested, pass nil to with_features.



216
217
218
219
220
221
222
223
224
225
226
227
# File 'lacci/lib/shoes/drawable.rb', line 216

def shoes_style_names(with_features: nil)
  # No with_features given? Use the ones requested by this Shoes::App
  with_features ||= @app.features
  parent_prop_names = self != Shoes::Drawable ? self.superclass.shoes_style_names(with_features:) : []

  if with_features == :all
    subclass_props = linkable_properties
  else
    subclass_props = linkable_properties.select { |prop| !prop[:feature] || with_features.include?(prop[:feature]) }
  end
  parent_prop_names | subclass_props.map { |prop| prop[:name] }
end

.shoes_styles(*names, feature: nil, &validator) ⇒ Object

Add these names as Shoes styles with the given validator and feature, if any



194
195
196
# File 'lacci/lib/shoes/drawable.rb', line 194

def shoes_styles(*names, feature: nil, &validator)
  names.each { |n| shoes_style(n, feature:, &validator) }
end

.unregister_drawable_id(id) ⇒ Object



148
149
150
151
# File 'lacci/lib/shoes/drawable.rb', line 148

def unregister_drawable_id(id)
  @drawables_by_id ||= {}
  @drawables_by_id.delete(id)
end

.use_current_appObject



252
253
254
255
256
# File 'lacci/lib/shoes/drawable.rb', line 252

def use_current_app
  cur_app = @current_app
  @current_app = nil
  cur_app
end

.validate_as(prop_name, value) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lacci/lib/shoes/drawable.rb', line 54

def validate_as(prop_name, value)
  prop_name = prop_name.to_s
  hashes = shoes_style_hashes

  h = hashes.detect { |hash| hash[:name] == prop_name }
  raise(Shoes::Errors::NoSuchStyleError, "Can't find property #{prop_name.inspect} in #{self} property list: #{hashes.inspect}!") unless h

  return value if h[:validator].nil?

  # Pass both the property name and value to the validator block
  h[:validator].call(value,prop_name)
end

.with_current_app(app) ⇒ Object

Current_app is set every time a drawable is created - we don't want to keep a default long because it's possible for apps to alternate who is creating. So make sure it's not kept long, and used up when used once.



244
245
246
247
248
249
250
# File 'lacci/lib/shoes/drawable.rb', line 244

def with_current_app(app)
  old_cur_app = @current_app
  @current_app = app
  ret = yield
  @current_app = old_cur_app
  ret
end

Instance Method Details

#app { ... } ⇒ Shoes::App

Calling stack.app or drawable.app will execute the block with the Shoes::App as self, and with that stack or flow as the current slot.

Yields:

  • the block to call with the Shoes App as self

Returns:

Incompatibilities with Shoes:

  • In Shoes Classic this is the only way to change self, while Scarpe will also change self with the other Slot Manipulation methods: #clear,

    append, #prepend, #before and #after.



434
435
436
437
# File 'lacci/lib/shoes/drawable.rb', line 434

def app(&block)
  @app.with_slot(self, &block) if block_given?
  @app
end

Return a banner-sized para. This can use all the normal Para styles and arguments. See Para#initialize for details.

Returns:



157
158
159
# File 'lacci/lib/shoes/drawables/para.rb', line 157

def banner(*args, **kwargs)
  para(*args, **{ size: :banner }.merge(kwargs))
end

#caption(*args, **kwargs) ⇒ Shoes::Para

Return a caption-sized para. This can use all the normal Para styles and arguments. See Para#initialize for details.

Returns:



193
194
195
# File 'lacci/lib/shoes/drawables/para.rb', line 193

def caption(*args, **kwargs)
  para(*args, **{ size: :caption }.merge(kwargs))
end

#destroyObject Also known as: remove

Removes the element from the Shoes::Drawable tree and removes all event subscriptions



558
559
560
561
562
563
564
565
# File 'lacci/lib/shoes/drawable.rb', line 558

def destroy
  @parent&.remove_child(self)
  @parent = nil
  @destroyed = true
  unsub_all_shoes_events
  send_shoes_event(event_name: "destroy", target: linkable_id)
  Shoes::Drawable.unregister_drawable_id(linkable_id)
end

#download(url, method: "GET", save: nil, styles: {}, &block) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lacci/lib/shoes/download.rb', line 21

def download(url, method: "GET", save: nil, styles: {}, &block)
  require "net/http"
  require "openssl"
  require "nokogiri"

  @block = block

  Thread.new do
    logger = Shoes::Log.logger("Shoes::App#download")
    begin
      uri = URI(url)
      response = perform_request(uri, method, styles)

      if response.is_a?(Net::HTTPRedirection)
        new_location = response["location"]
        new_uri = URI(new_location)
        response = perform_request(new_uri, method, styles)
      end

      wrapped_response = ResponseWrapper.new(response) # Wrap the response
      handle_response(wrapped_response, save, styles)
    rescue Net::HTTPError, Net::OpenTimeout, Net::ReadTimeout => e
      handle_error(e, logger)
    rescue StandardError => e
      handle_error(e.message, logger) # Pass the error message as a string
    end
  end
end

#event(event_name, *args, **kwargs) ⇒ Object



484
485
486
487
488
# File 'lacci/lib/shoes/drawable.rb', line 484

def event(event_name, *args, **kwargs)
  validate_event_name(event_name)

  send_shoes_event(*args, **kwargs, event_name:, target: linkable_id)
end

#hideObject

Hide the drawable.



569
570
571
# File 'lacci/lib/shoes/drawable.rb', line 569

def hide
  self.hidden = true
end

#hover { ... } ⇒ Object

Set the hover handler. Not every drawable may do something useful with this.

Yields:

  • A block to be called when the cursor moves to be over the drawable.



586
587
588
# File 'lacci/lib/shoes/drawable.rb', line 586

def hover(&block)
  @hover = block
end

#inscription(*args, **kwargs) ⇒ Shoes::Para

Return an inscription-sized para. This can use all the normal Para styles and arguments. See Para#initialize for details.

Returns:



202
203
204
# File 'lacci/lib/shoes/drawables/para.rb', line 202

def inscription(*args, **kwargs)
  para(*args, **{ size: :inscription }.merge(kwargs))
end

#inspectObject



453
454
455
456
457
# File 'lacci/lib/shoes/drawable.rb', line 453

def inspect
  "#<#{debug_id} " +
    " @parent=#{@parent ? @parent.debug_id : "(none)"} " +
    "@children=#{@children ? @children.map(&:debug_id) : "(none)"} properties=#{shoes_style_values.inspect}>"
end

#leave { ... } ⇒ Object

Set the leave handler. Not every drawable may do something useful with this.

Yields:

  • A block to be called when the cursor moves to be off of the drawable.



593
594
595
# File 'lacci/lib/shoes/drawable.rb', line 593

def leave(&block)
  @leave = block
end

#motion { ... } ⇒ Object

Set the motion handler, called with x and y coordinates as params. Not every drawable may do something useful with this.

Yields:

  • A block to be called when the cursor moves around over the drawable.



601
602
603
# File 'lacci/lib/shoes/drawable.rb', line 601

def motion(&block)
  @motion = block
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


637
638
639
640
641
642
643
644
# File 'lacci/lib/shoes/drawable.rb', line 637

def respond_to_missing?(name, include_private = false)
  name_s = name.to_s
  return true if self.class.shoes_style_name?(name_s)
  return true if self.class.shoes_style_name?(name_s[0..-2]) && name_s[-1] == "="
  return true if Drawable.drawable_class_by_name(name_s)

  super
end

#set_parent(new_parent, notify: true) ⇒ Object

Set the Drawable's parent drawable. Notify the display service that the parent has changed unless instructed not to. We don't notify when first creating the Drawable. The create event that is first sent will include the parent.



548
549
550
551
552
553
554
555
# File 'lacci/lib/shoes/drawable.rb', line 548

def set_parent(new_parent, notify: true)
  @parent&.remove_child(self)
  new_parent&.add_child(self)
  @parent = new_parent
  return unless notify

  send_shoes_event(new_parent&.linkable_id, event_name: "parent", target: linkable_id)
end

#shoes_style_values(with_features: @app.features) ⇒ Object



490
491
492
493
494
495
496
497
498
499
# File 'lacci/lib/shoes/drawable.rb', line 490

def shoes_style_values(with_features: @app.features)
  all_property_names = self.class.shoes_style_names(with_features:)

  properties = {}
  all_property_names.each do |prop|
    properties[prop] = instance_variable_get("@" + prop)
  end
  properties["shoes_linkable_id"] = self.linkable_id
  properties
end

#showObject

Show the drawable.



574
575
576
# File 'lacci/lib/shoes/drawable.rb', line 574

def show
  self.hidden = false
end

#style(*args, **kwargs) ⇒ Object



501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
# File 'lacci/lib/shoes/drawable.rb', line 501

def style(*args, **kwargs)
  if args.empty? && kwargs.empty?
    # Just called as .style()
    shoes_style_values
  elsif args.empty?
    # This is called to set one or more Shoes styles
    prop_names = self.class.shoes_style_names
    unknown_styles = kwargs.keys.select { |k| !prop_names.include?(k.to_s) }
    unless unknown_styles.empty?
      raise Shoes::Errors::NoSuchStyleError, "Unknown styles for drawable type #{self.class.name}: #{unknown_styles.join(", ")}"
    end

    kwargs.each do |name, val|
      instance_variable_set("@#{name}", val)
    end
  elsif args.length == 1 && args[0] < Shoes::Drawable
    # Shoes supports calling .style with a Shoes class, e.g. .style(Shoes::Button, displace_left: 5)
    kwargs.each do |name, val|
      Shoes::Drawable.drawable_default_styles[args[0]][name.to_sym] = val
    end
  else
    raise Shoes::Errors::InvalidAttributeValueError, "Unexpected arguments to style! args: #{args.inspect}, keyword args: #{kwargs.inspect}"
  end
end

#subtitle(*args, **kwargs) ⇒ Shoes::Para

Return a subtitle-sized para. This can use all the normal Para styles and arguments. See Para#initialize for details.

Returns:



175
176
177
# File 'lacci/lib/shoes/drawables/para.rb', line 175

def subtitle(*args, **kwargs)
  para(*args, **{ size: :subtitle }.merge(kwargs))
end

#tagline(*args, **kwargs) ⇒ Shoes::Para

Return a tagline-sized para. This can use all the normal Para styles and arguments. See Para#initialize for details.

Returns:



184
185
186
# File 'lacci/lib/shoes/drawables/para.rb', line 184

def tagline(*args, **kwargs)
  para(*args, **{ size: :tagline }.merge(kwargs))
end

#title(*args, **kwargs) ⇒ Shoes::Para

Return a title-sized para. This can use all the normal Para styles and arguments. See Para#initialize for details.

Returns:



166
167
168
# File 'lacci/lib/shoes/drawables/para.rb', line 166

def title(*args, **kwargs)
  para(*args, **{ size: :title }.merge(kwargs))
end

#toggleObject

Hide the drawable if it is currently shown. Show it if it is currently hidden.



579
580
581
# File 'lacci/lib/shoes/drawable.rb', line 579

def toggle
  self.hidden = !self.hidden
end