context 0.0.16 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- 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
|