lib/android: remove popup hack in `ui`
[nit.git] / lib / android / ui.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Views and services to use the Android native user interface
18 #
19 # Events, such as a button click, come from the UI thread and are then
20 # passed to the main thread. It is recommended to specialize one of the
21 # methods of the main thread to customize the response to a given event.
22 #
23 # This graph shows the path of a button click:
24 # ~~~raw
25 # UI Thread # Main thread
26 #
27 # User
28 # |
29 # V
30 # Button::click_ui --> Button::click
31 # |
32 # V
33 # App::catch_event
34 # ~~~
35 module ui is min_api_version 14
36
37 import native_app_glue
38 import pthreads::concurrent_collections
39
40 in "Java" `{
41 import android.app.Activity;
42
43 import android.view.Gravity;
44 import android.view.MotionEvent;
45 import android.view.ViewGroup;
46 import android.view.ViewGroup.MarginLayoutParams;
47
48 import android.widget.Button;
49 import android.widget.LinearLayout;
50 import android.widget.GridLayout;
51 import android.widget.PopupWindow;
52 import android.widget.TextView;
53
54 import java.lang.*;
55 import java.util.*;
56 `}
57
58 # An event from the `app.nit` framework
59 interface AppEvent
60 # Reaction to this event
61 fun react do end
62 end
63
64 # A control click event
65 class ClickEvent
66 super AppEvent
67
68 # Sender of this event
69 var sender: Button
70
71 redef fun react do sender.click self
72 end
73
74 # Receiver of events not handled directly by the sender
75 interface EventCatcher
76 fun catch_event(event: AppEvent) do end
77 end
78
79 redef class App
80 super EventCatcher
81
82 # Queue of events to be received by the main thread
83 var event_queue = new ConcurrentList[AppEvent]
84
85 # Call `react` on all `AppEvent` available in `event_queue`
86 protected fun loop_on_ui_callbacks
87 do
88 var queue = event_queue
89 while not queue.is_empty do
90 var event = queue.pop
91 event.react
92 end
93 end
94
95 redef fun run
96 do
97 loop
98 # Process Android events
99 poll_looper 100
100
101 # Process app.nit events
102 loop_on_ui_callbacks
103 end
104 end
105 end
106
107 redef extern class NativeActivity
108
109 # Set the main layout of this activity
110 fun content_view=(layout: NativeViewGroup) in "Java" `{
111 final ViewGroup final_layout = layout;
112 final Activity final_recv = recv;
113
114 recv.runOnUiThread(new Runnable() {
115 @Override
116 public void run() {
117 final_recv.setContentView(final_layout);
118
119 final_layout.requestFocus();
120 }
121 });
122 `}
123 end
124
125 # An `Object` that raises events
126 abstract class Eventful
127 var event_catcher: EventCatcher = app is lazy, writable
128 end
129
130 #
131 ## Nity classes and services
132 #
133
134 # An Android control with text
135 abstract class TextView
136 super Finalizable
137 super Eventful
138
139 # Native Java variant to this Nity class
140 type NATIVE: NativeTextView
141
142 # The native Java object encapsulated by `self`
143 var native: NATIVE is noinit
144
145 # Get the text of this view
146 fun text: String
147 do
148 var jstr = native.text
149 var str = jstr.to_s
150 jstr.delete_local_ref
151 return str
152 end
153
154 # Set the text of this view
155 fun text=(value: Text)
156 do
157 var jstr = value.to_s.to_java_string
158 native.text = jstr
159 jstr.delete_local_ref
160 end
161
162 # Get whether this view is enabled or not
163 fun enabled: Bool do return native.enabled
164
165 # Set if this view is enabled
166 fun enabled=(val: Bool) do native.enabled = val
167
168 # Set the size of the text in this view at `dpi`
169 fun text_size=(dpi: Numeric) do native.text_size = dpi.to_f
170
171 private var finalized = false
172 redef fun finalize
173 do
174 if not finalized then
175 native.delete_global_ref
176 finalized = true
177 end
178 end
179 end
180
181 # An Android button
182 class Button
183 super TextView
184
185 redef type NATIVE: NativeButton
186
187 init
188 do
189 var native = new NativeButton(app.native_activity, app.event_queue, self)
190 self.native = native.new_global_ref
191 end
192
193 # Click event on the Main thread
194 #
195 # By default, this method calls `app.catch_event`. It can be specialized
196 # with custom behavior or the receiver of `catch_event` can be changed
197 # with `event_catcher=`.
198 fun click(event: AppEvent) do event_catcher.catch_event(event)
199
200 # Click event on the UI thread
201 #
202 # This method is called on the UI thread and redirects the event to `click`
203 # throught `App::event_queue`. In most cases, you should implement `click`
204 # and leave `click_ui` as is.
205 fun click_ui do app.event_queue.add(new ClickEvent(self))
206 end
207
208 # An Android editable text field
209 class EditText
210 super TextView
211
212 redef type NATIVE: NativeEditText
213
214 init
215 do
216 var native = new NativeEditText(app.native_activity)
217 self.native = native.new_global_ref
218 end
219 end
220
221 #
222 ## Native classes
223 #
224
225 # A `View` for Android
226 extern class NativeView in "Java" `{ android.view.View `}
227 super JavaObject
228
229 fun minimum_width=(val: Int) in "Java" `{ recv.setMinimumWidth((int)val); `}
230 fun minimum_height=(val: Int) in "Java" `{ recv.setMinimumHeight((int)val); `}
231 end
232
233 # A collection of `NativeView`
234 extern class NativeViewGroup in "Java" `{ android.view.ViewGroup `}
235 super NativeView
236
237 fun add_view(view: NativeView) in "Java" `{ recv.addView(view); `}
238 end
239
240 # A `NativeViewGroup` organized in a line
241 extern class NativeLinearLayout in "Java" `{ android.widget.LinearLayout `}
242 super NativeViewGroup
243
244 new(context: NativeActivity) in "Java" `{ return new LinearLayout(context); `}
245
246 fun set_vertical in "Java" `{ recv.setOrientation(LinearLayout.VERTICAL); `}
247 fun set_horizontal in "Java" `{ recv.setOrientation(LinearLayout.HORIZONTAL); `}
248
249 redef fun add_view(view) in "Java"
250 `{
251 MarginLayoutParams params = new MarginLayoutParams(
252 LinearLayout.LayoutParams.MATCH_PARENT,
253 LinearLayout.LayoutParams.WRAP_CONTENT);
254 recv.addView(view, params);
255 `}
256
257 fun add_view_with_weight(view: NativeView, weight: Float)
258 in "Java" `{
259 recv.addView(view, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, (float)weight));
260 `}
261 end
262
263 # A `NativeViewGroup` organized as a grid
264 extern class NativeGridLayout in "Java" `{ android.widget.GridLayout `}
265 super NativeViewGroup
266
267 new(context: NativeActivity) in "Java" `{ return new android.widget.GridLayout(context); `}
268
269 fun row_count=(val: Int) in "Java" `{ recv.setRowCount((int)val); `}
270
271 fun column_count=(val: Int) in "Java" `{ recv.setColumnCount((int)val); `}
272
273 redef fun add_view(view) in "Java" `{ recv.addView(view); `}
274 end
275
276 extern class NativePopupWindow in "Java" `{ android.widget.PopupWindow `}
277 super NativeView
278
279 new (context: NativeActivity) in "Java" `{
280 PopupWindow recv = new PopupWindow(context);
281 recv.setWindowLayoutMode(LinearLayout.LayoutParams.MATCH_PARENT,
282 LinearLayout.LayoutParams.MATCH_PARENT);
283 recv.setClippingEnabled(false);
284 return recv;
285 `}
286
287 fun content_view=(layout: NativeViewGroup) in "Java" `{ recv.setContentView(layout); `}
288 end
289
290 extern class NativeTextView in "Java" `{ android.widget.TextView `}
291 super NativeView
292
293 new (context: NativeActivity) in "Java" `{ return new TextView(context); `}
294
295 fun text: JavaString in "Java" `{ return recv.getText().toString(); `}
296
297 fun text=(value: JavaString) in "Java" `{
298
299 final TextView final_recv = recv;
300 final String final_value = value;
301
302 ((Activity)recv.getContext()).runOnUiThread(new Runnable() {
303 @Override
304 public void run() {
305 final_recv.setText(final_value);
306 }
307 });
308 `}
309
310 fun enabled: Bool in "Java" `{ return recv.isEnabled(); `}
311 fun enabled=(value: Bool) in "Java" `{
312 final TextView final_recv = recv;
313 final boolean final_value = value;
314
315 ((Activity)recv.getContext()).runOnUiThread(new Runnable() {
316 @Override
317 public void run() {
318 final_recv.setEnabled(final_value);
319 }
320 });
321 `}
322
323 fun gravity_center in "Java" `{
324 recv.setGravity(Gravity.CENTER);
325 `}
326
327 fun text_size=(dpi: Float) in "Java" `{
328 recv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_DIP, (float)dpi);
329 `}
330 end
331
332 extern class NativeEditText in "Java" `{ android.widget.EditText `}
333 super NativeTextView
334
335 redef type SELF: NativeEditText
336
337 new (context: NativeActivity) in "Java" `{ return new android.widget.EditText(context); `}
338
339 fun width=(val: Int) in "Java" `{ recv.setWidth((int)val); `}
340
341 fun input_type_text in "Java" `{ recv.setInputType(android.text.InputType.TYPE_CLASS_TEXT); `}
342
343 redef fun new_global_ref: SELF import sys, Sys.jni_env `{
344 Sys sys = NativeEditText_sys(recv);
345 JNIEnv *env = Sys_jni_env(sys);
346 return (*env)->NewGlobalRef(env, recv);
347 `}
348 end
349
350 extern class NativeButton in "Java" `{ android.widget.Button `}
351 super NativeTextView
352
353 redef type SELF: NativeButton
354
355 new (context: NativeActivity, queue: ConcurrentList[AppEvent], sender_object: Object) import Button.click_ui in "Java" `{
356 final int final_sender_object = sender_object;
357
358 return new Button(context){
359 @Override
360 public boolean onTouchEvent(MotionEvent event) {
361 if(event.getAction() == MotionEvent.ACTION_DOWN) {
362 Button_click_ui(final_sender_object);
363 return true;
364 }
365 return false;
366 }
367 };
368 `}
369
370 redef fun new_global_ref: SELF import sys, Sys.jni_env `{
371 Sys sys = NativeButton_sys(recv);
372 JNIEnv *env = Sys_jni_env(sys);
373 return (*env)->NewGlobalRef(env, recv);
374 `}
375 end