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.
Files changed (117) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +679 -0
  3. data/README +188 -0
  4. data/Rakefile.rb +64 -18
  5. data/lib/Context/Bridge.rb +7 -6
  6. data/lib/Context/Context.rb +14 -35
  7. data/lib/Context/Gtk/Widget.rb +139 -33
  8. data/lib/Context/Log.rb +10 -0
  9. data/lib/Context/Publisher.rb +108 -0
  10. data/lib/Context/Spec.rb +8 -0
  11. data/lib/Context/Version.rb +1 -1
  12. data/lib/Context/View.rb +16 -15
  13. data/lib/Context/Views/Gtk/PageView.rb +3 -23
  14. data/lib/Context/Views/Gtk/Widgets/MainWindow.rb +70 -0
  15. data/lib/Context/Views/Gtk/Widgets/VBox.rb +29 -0
  16. data/lib/Context/Widget.rb +18 -27
  17. data/spec/Context/Bridge_spec.rb +19 -20
  18. data/spec/Context/Context_spec.rb +77 -63
  19. data/spec/Context/Gtk/Widget_spec.rb +23 -23
  20. data/spec/Context/Publisher_spec.rb +53 -0
  21. data/spec/Context/View_spec.rb +10 -14
  22. data/spec/Context/Views/Gtk/PageView_spec.rb +25 -9
  23. data/spec/Context/require_all_spec.rb +14 -7
  24. data/test_results.html +84 -121
  25. metadata +74 -128
  26. data/coverage/-usr-local-lib-site_ruby-1_8-atk_rb.html +0 -612
  27. data/coverage/-usr-local-lib-site_ruby-1_8-gdk_pixbuf2_rb.html +0 -637
  28. data/coverage/-usr-local-lib-site_ruby-1_8-glib2_rb.html +0 -829
  29. data/coverage/-usr-local-lib-site_ruby-1_8-gtk2-base_rb.html +0 -709
  30. data/coverage/-usr-local-lib-site_ruby-1_8-gtk2_rb.html +0 -623
  31. data/coverage/-usr-local-lib-site_ruby-1_8-pango_rb.html +0 -665
  32. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-color_rb.html +0 -865
  33. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-colors_rb.html +0 -1266
  34. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-constants_rb.html +0 -632
  35. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-blur_rb.html +0 -655
  36. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-circle_rb.html +0 -619
  37. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-color_rb.html +0 -621
  38. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-path_rb.html +0 -726
  39. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-rectangle_rb.html +0 -643
  40. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context-triangle_rb.html +0 -622
  41. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-context_rb.html +0 -639
  42. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-paper_rb.html +0 -793
  43. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-papers_rb.html +0 -651
  44. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-path_rb.html +0 -625
  45. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo-point_rb.html +0 -617
  46. data/coverage/-var-lib-gems-1_8-gems-cairo-1_7_0-src-lib-cairo_rb.html +0 -756
  47. data/coverage/index.html +0 -1170
  48. data/coverage/lib-Context-Bridge_rb.html +0 -673
  49. data/coverage/lib-Context-Context_rb.html +0 -756
  50. data/coverage/lib-Context-EnumeratedType_rb.html +0 -661
  51. data/coverage/lib-Context-ExecutionProxy_rb.html +0 -671
  52. data/coverage/lib-Context-Gtk-App_rb.html +0 -638
  53. data/coverage/lib-Context-Gtk-Key_rb.html +0 -664
  54. data/coverage/lib-Context-Gtk-Widget_rb.html +0 -667
  55. data/coverage/lib-Context-KeyAssignment_rb.html +0 -641
  56. data/coverage/lib-Context-KeyMap_rb.html +0 -653
  57. data/coverage/lib-Context-Key_rb.html +0 -667
  58. data/coverage/lib-Context-View_rb.html +0 -659
  59. data/coverage/lib-Context-Views-Gtk-PageView_rb.html +0 -660
  60. data/coverage/lib-Context-Views-PageView_rb.html +0 -627
  61. data/coverage/lib-Context-Widget_rb.html +0 -650
  62. data/coverage/lib-Context-require_all_rb.html +0 -623
  63. data/doc/classes/Context.html +0 -174
  64. data/doc/classes/Context/Bridge.html +0 -296
  65. data/doc/classes/Context/Context.html +0 -564
  66. data/doc/classes/Context/EnumeratedType.html +0 -283
  67. data/doc/classes/Context/ExecutionProxy.html +0 -228
  68. data/doc/classes/Context/Gtk.html +0 -126
  69. data/doc/classes/Context/Gtk/App.html +0 -217
  70. data/doc/classes/Context/Gtk/Key.html +0 -292
  71. data/doc/classes/Context/Gtk/PageView.html +0 -245
  72. data/doc/classes/Context/Gtk/PageView/MainWindow.html +0 -200
  73. data/doc/classes/Context/Gtk/Widget.html +0 -311
  74. data/doc/classes/Context/Key.html +0 -305
  75. data/doc/classes/Context/Key/Modifier.html +0 -117
  76. data/doc/classes/Context/KeyAssignment.html +0 -234
  77. data/doc/classes/Context/KeyMap.html +0 -273
  78. data/doc/classes/Context/PageView.html +0 -180
  79. data/doc/classes/Context/View.html +0 -334
  80. data/doc/classes/Context/Widget.html +0 -298
  81. data/doc/classes/Kernel.html +0 -148
  82. data/doc/classes/Object.html +0 -196
  83. data/doc/created.rid +0 -1
  84. data/doc/files/lib/Context/Bridge_rb.html +0 -108
  85. data/doc/files/lib/Context/Context_rb.html +0 -109
  86. data/doc/files/lib/Context/EnumeratedType_rb.html +0 -101
  87. data/doc/files/lib/Context/ExecutionProxy_rb.html +0 -101
  88. data/doc/files/lib/Context/Gtk/App_rb.html +0 -110
  89. data/doc/files/lib/Context/Gtk/Key_rb.html +0 -109
  90. data/doc/files/lib/Context/Gtk/Widget_rb.html +0 -109
  91. data/doc/files/lib/Context/KeyAssignment_rb.html +0 -108
  92. data/doc/files/lib/Context/KeyMap_rb.html +0 -109
  93. data/doc/files/lib/Context/Key_rb.html +0 -108
  94. data/doc/files/lib/Context/Version_rb.html +0 -101
  95. data/doc/files/lib/Context/View_rb.html +0 -101
  96. data/doc/files/lib/Context/Views/Gtk/PageView_rb.html +0 -111
  97. data/doc/files/lib/Context/Views/PageView_rb.html +0 -108
  98. data/doc/files/lib/Context/Widget_rb.html +0 -101
  99. data/doc/files/lib/Context/require_all_rb.html +0 -101
  100. data/doc/fr_class_index.html +0 -46
  101. data/doc/fr_file_index.html +0 -42
  102. data/doc/fr_method_index.html +0 -103
  103. data/doc/index.html +0 -24
  104. data/doc/rdoc-style.css +0 -208
  105. data/lib/Context/EnumeratedType.rb +0 -51
  106. data/lib/Context/ExecutionProxy.rb +0 -61
  107. data/lib/Context/Gtk/Key.rb +0 -54
  108. data/lib/Context/Key.rb +0 -57
  109. data/lib/Context/KeyAssignment.rb +0 -31
  110. data/lib/Context/KeyMap.rb +0 -43
  111. data/spec/Context/EnumeratedType_spec.rb +0 -51
  112. data/spec/Context/ExecutionProxy_spec.rb +0 -28
  113. data/spec/Context/Gtk/Key_spec.rb +0 -45
  114. data/spec/Context/KeyAssignment_spec.rb +0 -26
  115. data/spec/Context/KeyMap_spec.rb +0 -64
  116. data/spec/Context/Key_spec.rb +0 -22
  117. 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.
@@ -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 => [:rcov, :rdoc]
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
- t.spec_opts = ["--format html:test_results.html"]
39
- t.ruby_opts = ruby_opts
56
+ t.spec_opts = spec_opts
57
+ t.ruby_opts = ruby_opts
40
58
  end
41
59
 
42
- ht = Spec::Rake::SpecTask.new(:heckle) do |t|
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 << '--inline-source' <<
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 = true
105
- pkg.need_tar = true
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
 
@@ -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
- begin
35
- mod.unless_nil do class_eval(symbol.to_s) end
36
- rescue
37
- nil
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.
@@ -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
@@ -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
- class Widget < Context::Widget
7
- attr_reader :mainWindow, :expandHeight, :expandWidth
8
- attr_writer :mainWindow, :expandHeight, :expandWidth
9
-
10
- class << self
11
- # Redefine this in tests so that the widgets don't get displayed
12
- # on the screen.
13
- def inTests
14
- false
15
- end
16
- end
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 initialize(delegate)
19
- super(delegate)
20
- @mainWindow = nil
34
+ def setupWidget
35
+ @gtkWidgetMainWindow = nil
21
36
  # Packing hints for the container
22
- @expandHeight = false
23
- @expandWidth = false
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
- @mainWindow = @delegate
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
- def add(widget)
31
- if !widget.delegate.class.ancestors.include?(Gtk::Window)
32
- widget.mainWindow = @mainWindow
33
- @delegate.add(widget.delegate)
34
- if !Widget.inTests
35
- @delegate.show_all
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.delegate.set_transient_for(@mainWindow)
40
- if !Widget.inTests
41
- widget.delegate.show_all
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
- def remove(widget)
47
- widget.mainWindow = nil
48
- if !widget.delegate.class.ancestors.include?(Gtk::Window)
49
- @delegate.remove(widget.delegate)
50
- if !Widget.inTests
51
- @delegate.show_all
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
- @delegate.grab_focus
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