context 0.0.16 → 0.0.22

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