context 0.0.16 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +1 -0
- data/COPYING +679 -0
- data/README +188 -0
- data/Rakefile.rb +64 -18
- data/lib/Context/Bridge.rb +7 -6
- data/lib/Context/Context.rb +14 -35
- data/lib/Context/Gtk/Widget.rb +139 -33
- data/lib/Context/Log.rb +10 -0
- data/lib/Context/Publisher.rb +108 -0
- data/lib/Context/Spec.rb +8 -0
- data/lib/Context/Version.rb +1 -1
- data/lib/Context/View.rb +16 -15
- data/lib/Context/Views/Gtk/PageView.rb +3 -23
- data/lib/Context/Views/Gtk/Widgets/MainWindow.rb +70 -0
- data/lib/Context/Views/Gtk/Widgets/VBox.rb +29 -0
- data/lib/Context/Widget.rb +18 -27
- data/spec/Context/Bridge_spec.rb +19 -20
- data/spec/Context/Context_spec.rb +77 -63
- data/spec/Context/Gtk/Widget_spec.rb +23 -23
- data/spec/Context/Publisher_spec.rb +53 -0
- data/spec/Context/View_spec.rb +10 -14
- data/spec/Context/Views/Gtk/PageView_spec.rb +25 -9
- data/spec/Context/require_all_spec.rb +14 -7
- data/test_results.html +84 -121
- metadata +74 -128
- data/coverage/-usr-local-lib-site_ruby-1_8-atk_rb.html +0 -612
- data/coverage/-usr-local-lib-site_ruby-1_8-gdk_pixbuf2_rb.html +0 -637
- data/coverage/-usr-local-lib-site_ruby-1_8-glib2_rb.html +0 -829
- data/coverage/-usr-local-lib-site_ruby-1_8-gtk2-base_rb.html +0 -709
- data/coverage/-usr-local-lib-site_ruby-1_8-gtk2_rb.html +0 -623
- data/coverage/-usr-local-lib-site_ruby-1_8-pango_rb.html +0 -665
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-color_rb.html +0 -865
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-colors_rb.html +0 -1266
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-constants_rb.html +0 -632
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-blur_rb.html +0 -655
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-circle_rb.html +0 -619
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-color_rb.html +0 -621
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-path_rb.html +0 -726
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-rectangle_rb.html +0 -643
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-triangle_rb.html +0 -622
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context_rb.html +0 -639
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-paper_rb.html +0 -793
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-papers_rb.html +0 -651
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-path_rb.html +0 -625
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-point_rb.html +0 -617
- data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo_rb.html +0 -756
- data/coverage/index.html +0 -1170
- data/coverage/lib-Context-Bridge_rb.html +0 -673
- data/coverage/lib-Context-Context_rb.html +0 -756
- data/coverage/lib-Context-EnumeratedType_rb.html +0 -661
- data/coverage/lib-Context-ExecutionProxy_rb.html +0 -671
- data/coverage/lib-Context-Gtk-App_rb.html +0 -638
- data/coverage/lib-Context-Gtk-Key_rb.html +0 -664
- data/coverage/lib-Context-Gtk-Widget_rb.html +0 -667
- data/coverage/lib-Context-KeyAssignment_rb.html +0 -641
- data/coverage/lib-Context-KeyMap_rb.html +0 -653
- data/coverage/lib-Context-Key_rb.html +0 -667
- data/coverage/lib-Context-View_rb.html +0 -659
- data/coverage/lib-Context-Views-Gtk-PageView_rb.html +0 -660
- data/coverage/lib-Context-Views-PageView_rb.html +0 -627
- data/coverage/lib-Context-Widget_rb.html +0 -650
- data/coverage/lib-Context-require_all_rb.html +0 -623
- data/doc/classes/Context.html +0 -174
- data/doc/classes/Context/Bridge.html +0 -296
- data/doc/classes/Context/Context.html +0 -564
- data/doc/classes/Context/EnumeratedType.html +0 -283
- data/doc/classes/Context/ExecutionProxy.html +0 -228
- data/doc/classes/Context/Gtk.html +0 -126
- data/doc/classes/Context/Gtk/App.html +0 -217
- data/doc/classes/Context/Gtk/Key.html +0 -292
- data/doc/classes/Context/Gtk/PageView.html +0 -245
- data/doc/classes/Context/Gtk/PageView/MainWindow.html +0 -200
- data/doc/classes/Context/Gtk/Widget.html +0 -311
- data/doc/classes/Context/Key.html +0 -305
- data/doc/classes/Context/Key/Modifier.html +0 -117
- data/doc/classes/Context/KeyAssignment.html +0 -234
- data/doc/classes/Context/KeyMap.html +0 -273
- data/doc/classes/Context/PageView.html +0 -180
- data/doc/classes/Context/View.html +0 -334
- data/doc/classes/Context/Widget.html +0 -298
- data/doc/classes/Kernel.html +0 -148
- data/doc/classes/Object.html +0 -196
- data/doc/created.rid +0 -1
- data/doc/files/lib/Context/Bridge_rb.html +0 -108
- data/doc/files/lib/Context/Context_rb.html +0 -109
- data/doc/files/lib/Context/EnumeratedType_rb.html +0 -101
- data/doc/files/lib/Context/ExecutionProxy_rb.html +0 -101
- data/doc/files/lib/Context/Gtk/App_rb.html +0 -110
- data/doc/files/lib/Context/Gtk/Key_rb.html +0 -109
- data/doc/files/lib/Context/Gtk/Widget_rb.html +0 -109
- data/doc/files/lib/Context/KeyAssignment_rb.html +0 -108
- data/doc/files/lib/Context/KeyMap_rb.html +0 -109
- data/doc/files/lib/Context/Key_rb.html +0 -108
- data/doc/files/lib/Context/Version_rb.html +0 -101
- data/doc/files/lib/Context/View_rb.html +0 -101
- data/doc/files/lib/Context/Views/Gtk/PageView_rb.html +0 -111
- data/doc/files/lib/Context/Views/PageView_rb.html +0 -108
- data/doc/files/lib/Context/Widget_rb.html +0 -101
- data/doc/files/lib/Context/require_all_rb.html +0 -101
- data/doc/fr_class_index.html +0 -46
- data/doc/fr_file_index.html +0 -42
- data/doc/fr_method_index.html +0 -103
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
- data/lib/Context/EnumeratedType.rb +0 -51
- data/lib/Context/ExecutionProxy.rb +0 -61
- data/lib/Context/Gtk/Key.rb +0 -54
- data/lib/Context/Key.rb +0 -57
- data/lib/Context/KeyAssignment.rb +0 -31
- data/lib/Context/KeyMap.rb +0 -43
- data/spec/Context/EnumeratedType_spec.rb +0 -51
- data/spec/Context/ExecutionProxy_spec.rb +0 -28
- data/spec/Context/Gtk/Key_spec.rb +0 -45
- data/spec/Context/KeyAssignment_spec.rb +0 -26
- data/spec/Context/KeyMap_spec.rb +0 -64
- data/spec/Context/Key_spec.rb +0 -22
- data/spec/Context/Widget_spec.rb +0 -15
data/README
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
= Context - Contextual UI Framework
|
|
2
|
+
|
|
3
|
+
Context is a contextual UI framework. It is based on the Model View
|
|
4
|
+
Presentor model. The idea is that you have model objects that
|
|
5
|
+
represent the core data in your application. You also have views that
|
|
6
|
+
represent the user interface input and output. Finally you have
|
|
7
|
+
"contexts" that represent a user situation in the application. The
|
|
8
|
+
logic that ties the models and views resides in the contexts. The
|
|
9
|
+
main advantages to this model are that you can easily write UI unit
|
|
10
|
+
tests and you can easily create bridge patterns for supporting
|
|
11
|
+
multiple widget sets (although only GTK+ is supported at the moment).
|
|
12
|
+
Context is intended to be extremely minimal. Only the top level
|
|
13
|
+
abstract classes are included. It is *not* a widget set! You have to
|
|
14
|
+
write your own models, views and contexts.
|
|
15
|
+
|
|
16
|
+
Below you will find some usage examples and a detailed discussion
|
|
17
|
+
of the design rationale.
|
|
18
|
+
|
|
19
|
+
== Usage Examples
|
|
20
|
+
|
|
21
|
+
== Design Rationale
|
|
22
|
+
|
|
23
|
+
Probably the most common design structure for user interface
|
|
24
|
+
design is Model, View, Controller (MVC). While most people are
|
|
25
|
+
familiar with this idiom, before I discuss Context I will
|
|
26
|
+
explain MVC. Hopefully this discussion will dispel possible
|
|
27
|
+
sources of confusion.
|
|
28
|
+
|
|
29
|
+
MVC is a common idiom intended to separate UI objects from
|
|
30
|
+
non-UI objects so as to create a cleaner design that's
|
|
31
|
+
easy to work with. In MVC there are three main types
|
|
32
|
+
of objects:
|
|
33
|
+
|
|
34
|
+
* Model - Objects that represent the problem domain abstracted from the UI.
|
|
35
|
+
* View - UI objects that represent how a user will *see* information.
|
|
36
|
+
* Controller - Objects that represent the handling of input from the user.
|
|
37
|
+
|
|
38
|
+
Exactly how the object model decomposition works, and where functionality
|
|
39
|
+
is put is a source of much confusion amongst programmers. And in fact,
|
|
40
|
+
many so called MVC widget sets make it impossible to create controller
|
|
41
|
+
objects at all. The following is a description of my view of good
|
|
42
|
+
MVC practice. Keep in mind that it may differ from your own experience
|
|
43
|
+
or practice. But to keep the discussion relevant it helps to have
|
|
44
|
+
a shared understanding of these issues.
|
|
45
|
+
|
|
46
|
+
The easiest place to start discussing is the view. The view represents
|
|
47
|
+
the visual representation of the data in the UI. So if you have
|
|
48
|
+
some text, it is the text pane and the character glyphs that the
|
|
49
|
+
user can see. But it becomes more confusing when we talk about
|
|
50
|
+
views which contain things like buttons. What data does a button
|
|
51
|
+
represent?
|
|
52
|
+
|
|
53
|
+
Let's sidestep that issue for a second and discuss the model. The
|
|
54
|
+
model contains the problem domain objects. So, if we have some data,
|
|
55
|
+
the data objects are the model. For textual data, we can create a
|
|
56
|
+
view consisting of text panes and text that display that data. The
|
|
57
|
+
situation gets more complicated when we discuss something like a text
|
|
58
|
+
editor. The text buffer is clearly a model object, but in most widget
|
|
59
|
+
sets there is a text input widget that contains a text buffer. Can we
|
|
60
|
+
use the widget's text input buffer for our model? If so, how does
|
|
61
|
+
that affect the separation of model from UI? Again, let's defer this
|
|
62
|
+
discussion for a few moments.
|
|
63
|
+
|
|
64
|
+
Finally, we have the controller. The controller represents the
|
|
65
|
+
input. I like to think of the controller as a state pattern
|
|
66
|
+
with commands. The UI sends user input to the controller.
|
|
67
|
+
This creates commands (e.g., delete a character) and the state
|
|
68
|
+
pattern interprets the commands, updating the model and views
|
|
69
|
+
in doing so. However, as with the view and model we run into
|
|
70
|
+
confusion. In most widget sets the input is handled by the individual
|
|
71
|
+
widgets (or worse some arbitrary code in the widget library).
|
|
72
|
+
This input can not be funnelled easily to a controller object
|
|
73
|
+
to create commands. Instead it updates it's own internal model
|
|
74
|
+
and view.
|
|
75
|
+
|
|
76
|
+
As you can see, in each case there is some confusion that
|
|
77
|
+
can lead to poor separation of model from UI. But I think there
|
|
78
|
+
is an even greater problem. Where does the problem domain
|
|
79
|
+
*logic* go in an MVC program? It might seem obvious that it
|
|
80
|
+
will go into the model. But from experience I have found this
|
|
81
|
+
is rarely the case. And with a little thought we can see why.
|
|
82
|
+
|
|
83
|
+
When we think of model objects, it's quite easy to see where
|
|
84
|
+
the data will go. And we can also put methods on that data
|
|
85
|
+
to create objects. But the real user requirements of the
|
|
86
|
+
program (i.e., the problem domain logic) often ends up in the
|
|
87
|
+
controller and views. Consider a user requirement for
|
|
88
|
+
a fictitious program:
|
|
89
|
+
|
|
90
|
+
When the user presses "delete", the user is asked if they
|
|
91
|
+
really want to delete the record. If they respond "yes",
|
|
92
|
+
then the record is deleted. In this case, the table
|
|
93
|
+
display turns red and the next record is displayed. If they
|
|
94
|
+
respond "no", the next record is displayed.
|
|
95
|
+
|
|
96
|
+
Where would you write this code? In the record object (which
|
|
97
|
+
is clearly a model object)? If so, how would you get the
|
|
98
|
+
record object to create the view asking the user if they
|
|
99
|
+
really want to delete the record? Then the controller receives
|
|
100
|
+
responses from the UI. How does it interrupt the logic
|
|
101
|
+
of the model to tell it what to do? And how/when do we update
|
|
102
|
+
the view for the table?
|
|
103
|
+
|
|
104
|
+
This is virtually impossible. And keep in mind that the model
|
|
105
|
+
almost certainly shouldn't know about the view and controller,
|
|
106
|
+
since it represents only problem domain information and not UI.
|
|
107
|
+
|
|
108
|
+
So the reality is that in complex MVC code, the problem domain
|
|
109
|
+
*data* is kept in the model objects, while the *logic* is kept
|
|
110
|
+
in the views and controllers. So if you wanted, for instance,
|
|
111
|
+
to adapt your program for a different widget library, you're pretty
|
|
112
|
+
much out of luck -- the logic is spread all through the UI
|
|
113
|
+
code.
|
|
114
|
+
|
|
115
|
+
The thesis of Context is that most problems with MVC arise from
|
|
116
|
+
it's inappropriate use. MVC works extremely well for simple
|
|
117
|
+
problems that don't contain a lot of logic. But it scales
|
|
118
|
+
poorly. Where MVC works exceptionally well is for building
|
|
119
|
+
widgets themselves.
|
|
120
|
+
|
|
121
|
+
A widget, for example a text input widget, is self contained.
|
|
122
|
+
You can have a model object representing a collection of
|
|
123
|
+
strings. You can have a view object representing how the
|
|
124
|
+
widget is displayed on the window. And you can have a
|
|
125
|
+
controller object that takes input and updates the model
|
|
126
|
+
and view.
|
|
127
|
+
|
|
128
|
+
Context's idea is to implement these UI widgets using MVC
|
|
129
|
+
and use a slightly *different* idiom for problem domain
|
|
130
|
+
logic and data. The following is Context's design for
|
|
131
|
+
doing this.
|
|
132
|
+
|
|
133
|
+
There are again 3 different types of objects:
|
|
134
|
+
|
|
135
|
+
* Model -- These are true problem domain objects that represent the state of the running program.
|
|
136
|
+
* Context -- This describes a user scenario with related UI. It creates the appropriate Views, handles input, modifies the Model objects appropriatly and generates output. It contains the user domain logic.
|
|
137
|
+
* View -- Though related, this is not a traditional View at all. It creates and maintains the MVC widgets for a visual context. It takes output from the context and updates the widgets. It takes input from the widgets, and sends it to the context.
|
|
138
|
+
|
|
139
|
+
Often there is a single View for each context. However, there is no
|
|
140
|
+
reason that a context can't have several Views, or that several
|
|
141
|
+
contexts can't share a View. Also, there is usually an abstract View
|
|
142
|
+
(which defines the input and output that it handles) and a concrete
|
|
143
|
+
View (which implements the real widgets).
|
|
144
|
+
|
|
145
|
+
Model objects are pretty self explanatory. The only issue is that
|
|
146
|
+
the Model objects should not reference any of the contexts nor
|
|
147
|
+
should they reference any of the Views or widgets. They are truly
|
|
148
|
+
Model domain objects.
|
|
149
|
+
|
|
150
|
+
Views are not really like MVC Views at all. The abstract Views simply
|
|
151
|
+
define the interface for both input and output. Output is generally
|
|
152
|
+
Model objects that need to be Viewed by the user. In the concrete
|
|
153
|
+
Views, the data from the Model objects is displayed in widgets.
|
|
154
|
+
Input consists of commands generated from input from the widgets.
|
|
155
|
+
Under no circumstances should the Views update a real Model object.
|
|
156
|
+
Instead they should create a command which it passed back to the
|
|
157
|
+
context which updates the Model.
|
|
158
|
+
|
|
159
|
+
You can think of the abstract View as the conduit for I/O. The
|
|
160
|
+
concrete View implements the endpoint for that conduit. As much
|
|
161
|
+
as possible, the View should contain no problem domain logic. Anything
|
|
162
|
+
that is a requirement for the user should be implemented in the
|
|
163
|
+
context. Automated acceptance tests should be written against
|
|
164
|
+
the context without regard to the concrete
|
|
165
|
+
|
|
166
|
+
The Context contains the problem domain logic. It is reposonsible for
|
|
167
|
+
creating and destroying the views, sending data for the View to
|
|
168
|
+
display and interpreting commands from the View's input. However,
|
|
169
|
+
because the Context has references only to the abstract Views, it is
|
|
170
|
+
shielded from the widget library implementation details. Thus, if you
|
|
171
|
+
wish to port the program to another widget library you simply need to
|
|
172
|
+
rewrite concrete View classes.
|
|
173
|
+
|
|
174
|
+
Note that the Model should never reference eithe the Context or the
|
|
175
|
+
View. The View may reference the Model objects to display them, but
|
|
176
|
+
it should not update them directly. If the View needs to update a
|
|
177
|
+
model object, it should create a command and send it to the context.
|
|
178
|
+
The Context can reference both the Model objects (including updating
|
|
179
|
+
them) *and* it can reference the abstract Views. It should not,
|
|
180
|
+
however, reference the concrete Views.
|
|
181
|
+
|
|
182
|
+
Note: In practice I rarely make actual command objects for the Views
|
|
183
|
+
to send to the Context. Instead I have them simply call a method on
|
|
184
|
+
the Context. In truth this somewhat breaks the model that the View
|
|
185
|
+
know nothing about the Context. However, since ruby is a message
|
|
186
|
+
passing language, a method call *is* a command of a sort. So I'll do
|
|
187
|
+
this for efficiency. In this case, you should implement
|
|
188
|
+
method_missing on the Context object.
|
data/Rakefile.rb
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
require 'rake'
|
|
2
|
+
require 'rubygems'
|
|
2
3
|
require 'spec/rake/spectask'
|
|
4
|
+
# Debian forces the installation of a very old version of rdoc
|
|
5
|
+
# in /usr/lib/ruby if you install rubygems. So I need to override
|
|
6
|
+
# it here.
|
|
7
|
+
gem 'rdoc', ">= 2.2"
|
|
8
|
+
require 'rdoc'
|
|
3
9
|
require 'rake/rdoctask'
|
|
4
|
-
require 'rubygems'
|
|
5
10
|
require 'rake/gempackagetask'
|
|
6
11
|
require 'rake/testtask'
|
|
12
|
+
require 'fileutils'
|
|
7
13
|
require 'lib/Context/Version'
|
|
8
14
|
|
|
15
|
+
#======================== Setup ================================
|
|
16
|
+
|
|
17
|
+
# Rubyforge details
|
|
18
|
+
rubyforge_project = "jldrill"
|
|
19
|
+
rubyforge_maintainer = "mikekchar@rubyforge.org"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Spec options
|
|
23
|
+
spec_opts = ['-f html:test_results.html']
|
|
9
24
|
spec_files = FileList[
|
|
10
25
|
'spec/**/*_spec.rb'
|
|
11
26
|
]
|
|
@@ -23,40 +38,34 @@ pkg_files = FileList[
|
|
|
23
38
|
]
|
|
24
39
|
|
|
25
40
|
|
|
26
|
-
task :default => [:
|
|
41
|
+
task :default => [:spec]
|
|
27
42
|
|
|
43
|
+
desc "Run the tests (default). Output goes to test_results.html"
|
|
28
44
|
st = Spec::Rake::SpecTask.new(:spec) do |t|
|
|
29
45
|
t.spec_files = spec_files
|
|
30
46
|
t.ruby_opts = ruby_opts
|
|
47
|
+
t.spec_opts = spec_opts
|
|
31
48
|
end
|
|
32
49
|
|
|
50
|
+
desc "Run the tests and find the code coverage. Test results are in test_results.html. Coverage is in coverage/index.html"
|
|
33
51
|
rc = Spec::Rake::SpecTask.new(:rcov) do |t|
|
|
34
52
|
t.spec_files = spec_files
|
|
35
53
|
t.rcov = true
|
|
36
54
|
t.rcov_opts = ["--exclude rspec", "--exclude rcov", "--exclude syntax",
|
|
37
55
|
"--exclude _spec"]
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
t.spec_opts = spec_opts
|
|
57
|
+
t.ruby_opts = ruby_opts
|
|
40
58
|
end
|
|
41
59
|
|
|
42
|
-
|
|
43
|
-
t.spec_files = spec_files
|
|
44
|
-
t.spec_opts = ["--heckle Context"]
|
|
45
|
-
t.ruby_opts = ruby_opts
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Build the RDOC documentation tree.
|
|
60
|
+
desc "Build the RDOC development documentation. output goes to doc/index.html"
|
|
49
61
|
rd = Rake::RDocTask.new(:rdoc) do |t|
|
|
50
62
|
t.rdoc_dir = 'doc'
|
|
51
63
|
t.title = "Context -- Contextual UI Framework"
|
|
52
|
-
t.options << '--
|
|
53
|
-
'--main' << 'Context' <<
|
|
64
|
+
t.options << '--main' << 'README' <<
|
|
54
65
|
'--title' << "Context -- Contextual UI Framework"
|
|
55
|
-
t.rdoc_files.include('./lib/**/*.rb')
|
|
66
|
+
t.rdoc_files.include('README', 'COPYING', 'AUTHORS', './lib/**/*.rb', './spec/**/*.rb')
|
|
56
67
|
end
|
|
57
68
|
|
|
58
|
-
# Build tar, zip and gem files.
|
|
59
|
-
# NOTE: The name of this task is automatically set to :package
|
|
60
69
|
gem_spec = Gem::Specification.new do |s|
|
|
61
70
|
|
|
62
71
|
#### Basic information.
|
|
@@ -89,6 +98,10 @@ gem_spec = Gem::Specification.new do |s|
|
|
|
89
98
|
# Use these for libraries.
|
|
90
99
|
s.require_path = 'lib'
|
|
91
100
|
|
|
101
|
+
#### Dependencies
|
|
102
|
+
|
|
103
|
+
s.add_dependency("gtk2")
|
|
104
|
+
|
|
92
105
|
#### Documentation and testing.
|
|
93
106
|
s.has_rdoc = true
|
|
94
107
|
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
|
|
@@ -98,10 +111,43 @@ gem_spec = Gem::Specification.new do |s|
|
|
|
98
111
|
s.author = "Mike Charlton"
|
|
99
112
|
s.email = "mikekchar@gmail.com"
|
|
100
113
|
s.homepage = "http://sakabatou.dnsdojo.org"
|
|
114
|
+
s.rubyforge_project = rubyforge_project
|
|
101
115
|
end
|
|
102
116
|
|
|
117
|
+
desc "Creates the gem files for context. Packages are placed in pkg and called context-<version>.gem."
|
|
103
118
|
package_task = Rake::GemPackageTask.new(gem_spec) do |pkg|
|
|
104
|
-
pkg.need_zip =
|
|
105
|
-
pkg.need_tar =
|
|
119
|
+
pkg.need_zip = false
|
|
120
|
+
pkg.need_tar = false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
desc "Cleans the debian tree."
|
|
124
|
+
task :clean_debian do
|
|
125
|
+
FileUtils.rm_rf "debian/libcontext-ruby"
|
|
126
|
+
FileUtils.rm_rf Dir.glob("debian/*debhelper*")
|
|
127
|
+
FileUtils.rm_rf "debian/files"
|
|
128
|
+
FileUtils.rm_rf "configure-stamp"
|
|
129
|
+
FileUtils.rm_rf "build-stamp"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
desc "Cleans everything for a pristine source directory."
|
|
133
|
+
task :clean => [:clobber_rcov, :clobber_rdoc, :clobber_package, :clean_debian]
|
|
134
|
+
|
|
135
|
+
desc "Create the debian source tree and copy the required files over. The files will end up in debian/libcontext-ruby"
|
|
136
|
+
task :debian_dir => [:clean_debian, :rdoc] do
|
|
137
|
+
# Create the new directory structure
|
|
138
|
+
FileUtils.mkdir_p "debian/libcontext-ruby/usr/lib/ruby/1.8"
|
|
139
|
+
FileUtils.mkdir_p "debian/libcontext-ruby/usr/share/doc/libcontext-ruby/html"
|
|
140
|
+
|
|
141
|
+
# Copy the libcontext-ruby files
|
|
142
|
+
FileUtils.cp_r Dir.glob("lib/*"), "debian/libcontext-ruby/usr/lib/ruby/1.8"
|
|
143
|
+
FileUtils.cp_r Dir.glob("doc/*"), "debian/libcontext-ruby/usr/share/doc/libcontext-ruby/html"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
desc "Clean everything, run tests, and build all the documentation."
|
|
147
|
+
task :build => [:clean, :rcov, :rdoc]
|
|
148
|
+
|
|
149
|
+
desc "Build a debian package. Note: This will *not* make a source package. Also, the .deb and .changes file will be put in the parent directory."
|
|
150
|
+
task :deb => [:clean_debian] do
|
|
151
|
+
sh "dpkg-buildpackage -b -tc -rfakeroot -i.bzr"
|
|
106
152
|
end
|
|
107
153
|
|
data/lib/Context/Bridge.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'Context/ExecutionProxy'
|
|
2
|
-
|
|
3
1
|
module Context
|
|
4
2
|
|
|
5
3
|
# This class is used to specify the namespace for a symbol
|
|
@@ -31,11 +29,14 @@ module Context
|
|
|
31
29
|
# Evaluate the module and symbol, returning the class.
|
|
32
30
|
# If it doesn't exist, return nil
|
|
33
31
|
def evalClass(mod, symbol)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
retVal = nil
|
|
33
|
+
if !mod.nil?
|
|
34
|
+
begin
|
|
35
|
+
retVal = mod.class_eval(symbol.to_s)
|
|
36
|
+
rescue
|
|
37
|
+
end
|
|
38
38
|
end
|
|
39
|
+
return retVal
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
# Returns true if the mod and symbol evaluate to a class in the system.
|
data/lib/Context/Context.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'Context/KeyMap'
|
|
2
|
-
require 'Context/ExecutionProxy'
|
|
3
1
|
|
|
4
2
|
module Context
|
|
5
3
|
|
|
@@ -18,7 +16,7 @@ module Context
|
|
|
18
16
|
# is called on enter(), not on Context creation. However, there is
|
|
19
17
|
# no requirement for this.
|
|
20
18
|
class Context
|
|
21
|
-
attr_reader :parent, :mainView
|
|
19
|
+
attr_reader :parent, :mainView, :viewBridge
|
|
22
20
|
attr_writer :mainView
|
|
23
21
|
|
|
24
22
|
# Create a new Context. Takes a Bridge that is used to
|
|
@@ -27,8 +25,8 @@ module Context
|
|
|
27
25
|
@parent = nil
|
|
28
26
|
@mainView = nil
|
|
29
27
|
@viewBridge = viewBridge
|
|
30
|
-
@keyMap = KeyMap.new
|
|
31
28
|
@entered = false
|
|
29
|
+
@onExitBlock = nil
|
|
32
30
|
end
|
|
33
31
|
|
|
34
32
|
# Creates the views for the context. This method is called
|
|
@@ -95,6 +93,15 @@ module Context
|
|
|
95
93
|
def isEntered?
|
|
96
94
|
@entered
|
|
97
95
|
end
|
|
96
|
+
|
|
97
|
+
# Set a block to be called when the context exits.
|
|
98
|
+
# Often a context is exited by the result of some UI activity
|
|
99
|
+
# at some unknown point in the future. The caller of the context
|
|
100
|
+
# may want to do something after the context exits. This block
|
|
101
|
+
# will be called at the end of the exit method.
|
|
102
|
+
def onExit(&block)
|
|
103
|
+
@onExitBlock = block
|
|
104
|
+
end
|
|
98
105
|
|
|
99
106
|
# Exits the Context. After it is called, the context is no longer
|
|
100
107
|
# active and can't be interacted with. This method automatically
|
|
@@ -109,38 +116,10 @@ module Context
|
|
|
109
116
|
@parent.mainView.removeView(@mainView) unless @mainView.nil?
|
|
110
117
|
end
|
|
111
118
|
destroyViews
|
|
119
|
+
if !@onExitBlock.nil?
|
|
120
|
+
@onExitBlock.call
|
|
121
|
+
end
|
|
112
122
|
end
|
|
113
123
|
|
|
114
|
-
# Sets a binding for a key.
|
|
115
|
-
# Whenever a key is pressed, the context is notified. This
|
|
116
|
-
# sets up a binding between the key and the block passed in.
|
|
117
|
-
# After that point, the block will be called everytime the
|
|
118
|
-
# key is pressed.
|
|
119
|
-
#
|
|
120
|
-
# Warning: This key binding mechanism is likely to be
|
|
121
|
-
# depracated, or at least heavily modified soon.
|
|
122
|
-
def setKeyBinding(key, &binding)
|
|
123
|
-
a = KeyAssignment.new(key, &binding)
|
|
124
|
-
@keyMap.add(a)
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Returns the existing binding for a key.
|
|
128
|
-
#
|
|
129
|
-
# Warning: This key binding mechanism is likely to be
|
|
130
|
-
# depracated, or at least heavily modified soon.
|
|
131
|
-
def getKeyBinding(key)
|
|
132
|
-
a = @keyMap.findKey(key)
|
|
133
|
-
a.unless_nil.action
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Called by the view to indicated that a key has been pressed.
|
|
137
|
-
# Runs the binding in the keymap, or returns nil if there
|
|
138
|
-
# isn't one.
|
|
139
|
-
#
|
|
140
|
-
# Warning: This key binding mechanism is likely to be
|
|
141
|
-
# depracated, or at least heavily modified soon.
|
|
142
|
-
def notifyKey(view, key)
|
|
143
|
-
@keyMap.unless_nil do press(key) end
|
|
144
|
-
end
|
|
145
124
|
end
|
|
146
125
|
end
|
data/lib/Context/Gtk/Widget.rb
CHANGED
|
@@ -1,57 +1,163 @@
|
|
|
1
1
|
require 'Context/Widget'
|
|
2
|
+
require 'Context/Log'
|
|
2
3
|
require 'gtk2'
|
|
3
4
|
|
|
4
5
|
module Context::Gtk
|
|
6
|
+
include Context::Widget
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
# This is a set of routines for translating the requests
|
|
9
|
+
# from Context to the specific widgit set.
|
|
10
|
+
#
|
|
11
|
+
# Note: If you wish your widget to be able to add and removed
|
|
12
|
+
# other widgets (i.e. if it can act as a container), then
|
|
13
|
+
# please define the following two methods on your object.
|
|
14
|
+
#
|
|
15
|
+
# gtkAddWidget(widget) -- simply adds the passed widget to
|
|
16
|
+
# the correct Gtk container. Normally
|
|
17
|
+
# this can be implemented using add().
|
|
18
|
+
# However, for some things like tables
|
|
19
|
+
# you will have to use other methods.
|
|
20
|
+
# gtkRemoveWidget(widget) -- removes the passed widget from
|
|
21
|
+
# the correct Gtk container. Again
|
|
22
|
+
# you will normally implement this
|
|
23
|
+
# with remove().
|
|
24
|
+
#
|
|
25
|
+
# The following also need to be defined if your widget is
|
|
26
|
+
# not derived from a Gtk:Widget.
|
|
27
|
+
#
|
|
28
|
+
# show_all() -- Displays the widgets.
|
|
29
|
+
|
|
30
|
+
module Widget
|
|
31
|
+
attr_reader :gtkWidgetMainWindow
|
|
32
|
+
attr_writer :gtkWidgetMainWindow
|
|
17
33
|
|
|
18
|
-
def
|
|
19
|
-
|
|
20
|
-
@mainWindow = nil
|
|
34
|
+
def setupWidget
|
|
35
|
+
@gtkWidgetMainWindow = nil
|
|
21
36
|
# Packing hints for the container
|
|
22
|
-
@
|
|
23
|
-
@
|
|
37
|
+
@gtkWidgetExpandHeight = false
|
|
38
|
+
@gtkWidgetExpandWidth = false
|
|
39
|
+
@gtkWidgetAddedToCallback = nil
|
|
40
|
+
@gtkWidgetRemovedFromCallback = nil
|
|
24
41
|
end
|
|
25
42
|
|
|
43
|
+
# Declares that this widget is a main Window
|
|
44
|
+
# Normally, this will get set for you if the widget you are
|
|
45
|
+
# adding is derived from Gtk::Window. But you can set it
|
|
46
|
+
# explicitly for certain effects if you know what you are doing.
|
|
26
47
|
def isAMainWindow
|
|
27
|
-
@
|
|
48
|
+
@gtkWidgetMainWindow = self
|
|
28
49
|
end
|
|
50
|
+
|
|
51
|
+
# When adding the widget, expand it to take up all the allocated
|
|
52
|
+
# vertical height.
|
|
53
|
+
def expandWidgetHeight
|
|
54
|
+
@gtkWidgetExpandHeight = true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns true when the the widget should take up all the allocated
|
|
58
|
+
# vertical height.
|
|
59
|
+
def expandWidgetHeight?
|
|
60
|
+
@gtkWidgetExpandHeight
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# When adding the widget, expand it to take up all the allocated
|
|
64
|
+
# horizontal width.
|
|
65
|
+
def expandWidgetWidth
|
|
66
|
+
@gtkWidgetExpandWidth = true
|
|
67
|
+
end
|
|
29
68
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
69
|
+
# Returns true when the the widget should take up all the allocated
|
|
70
|
+
# horizontal width.
|
|
71
|
+
def expandWidgetWidth?
|
|
72
|
+
@gtkWidgetExpandWidth
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Use this widget as a container for the passed widget.
|
|
76
|
+
# Calls gtkAddWidget, which you must define on the object.
|
|
77
|
+
# Normally this will simply call add() in the correct container.
|
|
78
|
+
# Also calls show_all, which you must define on the object
|
|
79
|
+
# (if it isn't already).
|
|
80
|
+
def addToThisWidget(widget)
|
|
81
|
+
if !widget.class.ancestors.include?(Gtk::Window)
|
|
82
|
+
widget.gtkWidgetMainWindow = @gtkWidgetMainWindow
|
|
83
|
+
gtkAddWidget(widget)
|
|
84
|
+
if !isInTests?
|
|
85
|
+
show_all
|
|
36
86
|
end
|
|
37
87
|
else
|
|
38
88
|
widget.isAMainWindow
|
|
39
|
-
widget.
|
|
40
|
-
if !
|
|
41
|
-
widget.
|
|
89
|
+
widget.set_transient_for(@gtkWidgetMainWindow)
|
|
90
|
+
if !isInTests?
|
|
91
|
+
widget.show_all
|
|
42
92
|
end
|
|
43
93
|
end
|
|
44
94
|
end
|
|
45
95
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
96
|
+
# Remove the passed widget from this object.
|
|
97
|
+
# Calls gtkRemoveWidget, which you must define on the object.
|
|
98
|
+
# Normally this will simply call remove().
|
|
99
|
+
# Also calls show_all, which you must define on the object
|
|
100
|
+
# (if it isn't already).
|
|
101
|
+
def removeFromThisWidget(widget)
|
|
102
|
+
widget.gtkWidgetMainWindow = nil
|
|
103
|
+
if !widget.class.ancestors.include?(Gtk::Window)
|
|
104
|
+
gtkRemoveWidget(widget)
|
|
105
|
+
if !isInTests?
|
|
106
|
+
show_all
|
|
52
107
|
end
|
|
53
108
|
end
|
|
54
|
-
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Set a closure to be run when the widget has been
|
|
112
|
+
# added. The block must accept the container widget
|
|
113
|
+
# as a parameter.
|
|
114
|
+
def afterWidgetIsAdded(&block)
|
|
115
|
+
@gtkWidgetAddedToCallback = block
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Set a closure to be run when the widget has been
|
|
119
|
+
# removed. The block must accept the container widget
|
|
120
|
+
# as a parameter.
|
|
121
|
+
def afterWidgetIsRemoved(&block)
|
|
122
|
+
@gtkWidgetRemovedFromCallback = block
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# This method is called after the widget has been
|
|
126
|
+
# successfully added to another widget
|
|
127
|
+
def widgetWasAddedTo(widget)
|
|
128
|
+
if !@gtkWidgetAddedToCallback.nil?
|
|
129
|
+
@gtkWidgetAddedToCallback.call(widget)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# This method is called after the widget has been
|
|
134
|
+
# successfully removed from another widget
|
|
135
|
+
def widgetWasRemovedFrom(widget)
|
|
136
|
+
if !@gtkWidgetRemovedFromCallback.nil?
|
|
137
|
+
@gtkWidgetRemovedFromCallback.call(widget)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Helper method for testing. If this method is redefined to
|
|
142
|
+
# return true, then the items will not be shown on the screen.
|
|
143
|
+
def isInTests?
|
|
144
|
+
false
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Default method for adding a widget. Simply logs an warning.
|
|
148
|
+
# It does not add the widget.
|
|
149
|
+
def gtkAddWidget(widget)
|
|
150
|
+
Context::Log::warning("Context::Widget",
|
|
151
|
+
"Attempted to add a widget " +
|
|
152
|
+
"to #{self.class} which doesn't define " +
|
|
153
|
+
"gtkAddWidget(). Ignoring addition.")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def gtkRemoveWidget(widget)
|
|
157
|
+
Context::Log::warning("Context::Widget",
|
|
158
|
+
"Attempted to remove a widget " +
|
|
159
|
+
"from #{self.class} which doesn't define " +
|
|
160
|
+
"gtkRemoveWidget(). Ignoring removal.")
|
|
55
161
|
end
|
|
56
162
|
end
|
|
57
163
|
end
|