[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [rgui-dev] binding only to exposure
From: |
Massimiliano Mirra |
Subject: |
Re: [rgui-dev] binding only to exposure |
Date: |
Fri, 16 Aug 2002 04:43:21 +0200 |
User-agent: |
Mutt/1.3.28i |
On Thu, Aug 08, 2002 at 09:02:41PM -0600, Tom Sawyer wrote:
> let me put the problem of only being able to bind to the exposed
> portions of an object via its accessor methods antoher way:
>
> class X
> attr_accessor :a, :b
> def initialize
> @a = 'string'
> @b = [ 'an', 'array' ]
> end
> end
>
> so here's a dummy class. now lets say i want to give this a gui such the
> user sees the values of a and b on the screen, with a as a label and b
> as a list (we'll leave out the window definition and all that jazz for
> the sake of brevity):
>
> x = X.new # Model/Logic/Core-Application (whatever name)
>
> la = Label.new; la.value = x.a
> lb = List.new; lb.items = x.b
>
> okay so far so good. now we wish to bind to them such that if a or b
> should change (by whatever means), the gui will reflect that.
>
> x.bind(:a=) { |x| la.value = x }
> x.bind(:b=) { |x| lb.items = x }
(As an aside and not strictly related to the subject of the post: the
meaning in the two lines of code above might well be made implicit in
the two lines of code below.)
>
> cool. when #a= or #b= is called the gui will reflect the change. now
> lets say that class X has an additonal method that does this:
>
> def update_from_query
> @a = query('a')
> @b << query('b')
> end
>
> with this we're going to run into a problem becasue @a and @b are being
> changed "beneath the hood" and our gui bindings won't catch it. our
> programmer might be told, it is incorrect form to use @ in the method
> and he should use the accessor methods instead. okay he says, i'll
> change it:
>
> def update_from_query
> self.a = query('a')
> self.b << query('b')
> end
>
> well, that helps, even though we had to alter our Model to work for the
> gui, at least now a is being caught. But b still is not. how do we
> account for that? the only means (that i can think of, at least) are to
> catch on update_from_query and modify all relevant bindings:
>
> x.bind(:update_from_query) {
> la.value = x.a
> lb.value = x.b
> }
>
> so our problem is solved, but i must ask, at what cost? this is a very
> simple dummy class we are working with. think of how involved it would
> be to do this for something complex. every method that modified any
> pertenant variables would require a binding with a proc specifying how
> each of those variables are to modify the gui. this can quickly become a
> very large task.
>
> for a simple example of what i mean just imagine trying to give a gui to
> the an object of type Array in and of itself. you'd have to bind on <<,
> sort!, compact!, reverse!, and on and on, for every method that altered
> the contents of the array, so that any changes would be "automagically"
> reflected/ this simply becomes too unweildy.
Here is a costly way (but we're implementing Observer, and Observer
almost always trades performance for simplicity):
class GuiManager
def initialize(model)
@model = model
end
def watch_changers(*methods)
methods.each do |method|
# append code to each watched method that calls the GuiManager
# change_event whenever that method is called
end
end
def set_update_proc(&block)
@update_proc = &block
end
def change_event
@update_proc.call
end
end
x = X.new
gui_manager = GuiManager.new(x)
gui_manager.watch_methods 'a=', 'b=', 'update_from_query', ...
gui_manager.set_update_proc do |model|
label.value = model.a
list.items = model.b
end
Now, when a `changer' method is called on the model, the update
procedure defined with set_update_proc is called immediately
afterwards and updates the interface. The setup is very simple.
The performance tradeoff is clear: all interface gets refreshed, even
though only a small part of the data has changed. This is very likely
to cause slowness in the bigger applications.
What workarounds can be used? Maybe dirty/clean flags somewhere to
tell the changed data (in need of interface update) from the
unchanged. Or a more precise (yet less quick) setup:
gui_manager.bind_setter_getter('a=', ['a'])
gui_manager.bind_setter_getter('b=', ['b'])
gui_manager.bind_setter_getter('update_from_query', ['a', 'b'])
Massimiliano