You could keep the @dialog
as an extension module variable and pass it into the observer constructor, ie:
# within create_html_dialog()
@observer = DcSelectionObserver.new(@dialog)
… and in the observer …
def initialize(dialog)
@dialog = dialog
end
… but read on.
First of all, there is no requirement for observers to be implemented as a class (or subclass) and then instantiated.
The only requirement is that observers be an object (which is easy because everything in Ruby is an object and a subclass of class Object
,) … and that any defined callbacks be publicly accessible.
Secondly, since the SU2016 observer overhaul 99% of observer superclasses are for documentation only and actually have no ancestor callback methods to pass down to child subclasses. NONE of the API’s various #add_observer
methods do any type checking to verify if an observer object is any particular subclass or has any particular ancestor class(es).
This means that any Ruby object can act as an observer. A weird example could be adding a singleton observer callback method to a Sketchup::Face
object and then attaching this face as an EntityObserver
to itself. (It’s not a very practical solution however.)
A more practical example (which I myself use often) is to use the extension submodule itself as an AppObserver
. Since there is always only one application object, there really is never any need to instantiate multiple AppObserver
objects within SketchUp’s running process.
This leads to the main question a coder should ask when implementing an observer. How many observers will be needed? If more than one, then the observer should be implemented as a class and instantiated as needed. Okay, when is more than one needed? The answer usually is if the observer needs to hold state within it that is unique to any given model object that may be open (think multiple models on the Mac platform) then normally a separate observer instance will be required per model.
If there was not to be any unique state (or data) held by the observer, then it is possible to implement a shared observer that can be attached to multiple models (or multiple objects owned by multiple models.)
So, in your case you have one dialog. This dialog would need to know when the active model was changed on the Mac so that the SelectionObserver
acts upon the selection collection of the active model. It may be possible then (if you stay with one single dialog,) to use the extension submodule as the selection observer object that acts upon the single dialog no matter which model is the active model.
This will mean that you also will need to implement some AppObserver
callbacks and attach the extension submodule to the application at the bottom of your files (probably within the “run once” block where you create UI objects.) Ie …
unless defined?(@loaded)
# UI commands, menu and toolbars
Sketchup.add_observer(self) # attach module as an AppObsever
@loaded = true
end
Then in the AppObserver
callbacks, you again attach the submodule as a selection observer to each model as it’s opened or created.
The beauty of this approach is that everything is in the same submodule and there is only one shared @dialog
reference. Also all the methods are at the same level and can be called without the clunky IDK_Programming::Dc_Ac
full qualification.
When you have a single object like a submodule acting as more than one kind of observer, this is called a “hybrid observer”. Recall I said that there really is no functionality in the API observer superclasses so there is no need to subclass them as there is no methods to inherit.
So you can have your extension submodule act as both an AppObserver
and a common SelectionObserver
.
For more information, often the best flavor of hybrid observers is a class that serves as the main observer object for model wide collections and is instantiated for each model that is created or opened. In the initialize
method for that class receives the model object as a parameter (from AppObsever
callbacks) and then proceeds to attach itself to each one of the model’s collection objects (selection, definitions, layers, materials, etc., etc.)
- P.S. - Try to keep code lines below 81 characters so we do not have to scroll horizontally.
Ruby uses 2 space indentation. Using larger indents contributes to excessive line length.