Merge: app.nit: intro `Text::open_in_browser` and example for the UI API
authorJean Privat <jean@pryen.org>
Mon, 27 Jun 2016 21:58:40 +0000 (17:58 -0400)
committerJean Privat <jean@pryen.org>
Mon, 27 Jun 2016 21:58:40 +0000 (17:58 -0400)
This PR intro a service to open a URL using the platform default browser, and an example for the UI API. The example uses most of the available controls, multiple windows and the new `open_in_browser` feature.

The changes to `core::exec` are updates for compatibility with `Text`, and some minor improvements to its doc.

Pull-Request: #2206
Reviewed-by: Jean Privat <jean@pryen.org>

lib/android/ui/ui.nit
lib/app/README.md
lib/app/examples/.gitignore
lib/app/examples/Makefile
lib/app/examples/http_request_example.nit
lib/app/examples/ui_example.nit [new file with mode: 0644]
lib/app/ui.nit
lib/core/exec.nit
lib/ios/ui/ui.nit
lib/linux/ui.nit

index cd9f4ea..7e14b19 100644 (file)
@@ -348,3 +348,18 @@ redef class Android_app_Fragment
                };
        `}
 end
+
+redef class Text
+       redef fun open_in_browser
+       do to_java_string.native_open_in_browser(app.native_activity)
+end
+
+redef class JavaString
+       private fun native_open_in_browser(context: NativeContext)
+       in "Java" `{
+               android.content.Intent intent = new android.content.Intent(
+                       android.content.Intent.ACTION_VIEW,
+                       android.net.Uri.parse(self));
+               context.startActivity(intent);
+       `}
+end
index d48d0b5..0e0187b 100644 (file)
@@ -68,8 +68,8 @@ So there is two ways  to customize the behavior on a given event:
 
 ## Usage Example
 
-The calculator example (at `../../examples/calculator/src/calculator.nit`) is a concrete,
-simple and complete use of the _app.nit_ portable UI.
+The example at `examples/ui_example.nit` shows off most features of `app::ui` in a minimal program.
+You can also take a look at the calculator (`../../examples/calculator/src/calculator.nit`) which is a concrete usage example.
 
 ## Platform-specific UI
 
index 7f0a15f..f594638 100644 (file)
@@ -1,3 +1,4 @@
 http_request_example
-http_request_example.apk
-http_request_example.app
+ui_example
+*.apk
+*.app
index f516f0d..2dfc094 100644 (file)
@@ -1,4 +1,8 @@
-all: http_request_example
+all: http_request_example ui_example
+
+android: http_request_example.apk ui_example.apk
+
+ios: http_request_example.app ui_example.app
 
 http_request_example: $(shell nitls -M http_request_example.nit linux)
        nitc http_request_example.nit -m linux
@@ -8,3 +12,15 @@ http_request_example.apk: $(shell nitls -M http_request_example.nit android)
 
 http_request_example.app: $(shell nitls -M http_request_example.nit ios)
        nitc http_request_example.nit -m ios
+
+ui_example: $(shell nitls -M ui_example.nit linux)
+       nitc ui_example.nit -m linux
+
+ui_example.apk: $(shell nitls -M ui_example.nit android)
+       nitc ui_example.nit -m android
+
+ui_example.app: $(shell nitls -M ui_example.nit ios)
+       nitc ui_example.nit -m ios
+
+clean:
+       rm -rf http_request_example http_request_example.apk http_request_example.app ui_example ui_example.apk ui_example.app
index 65c3db8..daa5314 100644 (file)
 # limitations under the License.
 
 # Example for the `app::http_request` main service `AsyncHttpRequest`
-module http_request_example
+module http_request_example is
+       app_name "app.nit HTTP"
+       app_namespace "org.nitlanguage.http_example"
+       android_api_target 15
+end
 
 import app::ui
 import app::http_request
+import android::aware # for android_api_target
 
 # Simple asynchronous HTTP request to http://example.com/ displaying feedback to the window
 class MyHttpRequest
@@ -56,7 +61,7 @@ class HttpRequestClientWindow
        super Window
 
        # Root layout
-       var layout = new VerticalLayout(parent=self)
+       var layout = new ListLayout(parent=self)
 
        # Button to send request
        var button_request = new Button(parent=layout, text="Press to send HTTP request")
diff --git a/lib/app/examples/ui_example.nit b/lib/app/examples/ui_example.nit
new file mode 100644 (file)
index 0000000..43628af
--- /dev/null
@@ -0,0 +1,92 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# User interface example using `app::ui`
+module ui_example is
+       app_name "app.nit UI"
+       app_namespace "org.nitlanguage.ui_example"
+       android_api_target 15
+end
+
+import app::ui
+import app::data_store
+import android::aware # for android_api_target
+
+# Window showing off some the available controls
+class UiExampleWindow
+       super Window
+
+       # Root layout
+       var layout = new ListLayout(parent=self)
+
+       # Some label
+       var some_label = new Label(parent=layout, text="This Window uses a ListLayout.")
+
+       # A checkbox
+       var checkbox = new CheckBox(parent=layout, text="A CheckBox")
+
+       # Horizontal organization
+       var h_layout = new HorizontalLayout(parent=layout)
+
+       # Description for the `user_input`
+       var user_input_label = new Label(parent=h_layout, text="Input some text:", align=0.5)
+
+       # Field for the user to enter data
+       var user_input = new TextInput(parent=h_layout, text="Default text")
+
+       # Button to open a new window with a ListLayout
+       var button_window = new Button(parent=layout, text="Open a new window")
+
+       # URL to open
+       var example_url = "http://nitlanguage.org/"
+
+       # Button to open the browser
+       var button_browser = new Button(parent=layout, text="Open {example_url}")
+
+       redef fun on_event(event)
+       do
+               if event isa ButtonPressEvent then
+                       if event.sender == button_browser then
+                               example_url.open_in_browser
+                       else if event.sender == button_window then
+                               app.push_window new SecondWindow
+                       end
+               else if event isa ToggleEvent then
+                       if event.sender == checkbox then checkbox.text = if checkbox.is_checked then "Checked" else "Unchecked"
+               end
+       end
+end
+
+# Another window with a small `VerticalLayout`
+class SecondWindow
+       super Window
+
+       # Root layout
+       var layout = new VerticalLayout(parent=self)
+
+       # Some label
+       var a_label = new Label(parent=layout, text="This window uses a VerticalLayout.")
+
+       # Another label
+       var another_label = new Label(parent=layout, text="Close it by tapping the back button.")
+end
+
+redef class App
+       redef fun on_create
+       do
+               # Create the main window
+               push_window new UiExampleWindow
+               super
+       end
+end
index 996629f..5f66ab4 100644 (file)
@@ -305,3 +305,8 @@ class ListLayout
        super View
        super CompositeControl
 end
+
+redef class Text
+       # Open the URL `self` with the default browser
+       fun open_in_browser do print_error "Text::open_in_browser not implemented on this platform."
+end
index 6c03c7f..c1edff8 100644 (file)
@@ -59,42 +59,44 @@ class Process
        end
 
        # The status once finished
+       #
+       # Require: `is_finished`
        fun status: Int
        do
                assert is_finished
                return data.status
        end
 
-       # The executable run
-       # Is a filepath, or a executable found in PATH
-       var command: String
+       # The target executable
+       # Either a file path or the name of an executable available in PATH.
+       var command: Text
 
        # The arguments of the command
        # Starts with the first real arguments---ie. does not include the progname (`argv[0]`, in C)
-       var arguments: nullable Array[String]
+       var arguments: nullable Array[Text]
 
        # Launch a command with some arguments
-       init(command: String, arguments: String...) is old_style_init do
+       init(command: Text, arguments: Text...) is old_style_init do
                self.command = command
                self.arguments = arguments
                execute
        end
 
        # Launch a simple command with arguments passed as an array
-       init from_a(command: String, arguments: nullable Array[String])
+       init from_a(command: Text, arguments: nullable Array[Text])
        do
                self.command = command
                self.arguments = arguments
                execute
        end
 
-       # flags used internally to know whith pipe to open
+       # Flags used internally to know which pipe to open
        private fun pipeflags: Int do return 0
 
        # Internal code to handle execution
        protected fun execute
        do
-               # The pass the arguments as a big C string where elements are separated with '\0'
+               # Pass the arguments as a big C string where elements are separated with '\0'
                var args = new FlatBuffer
                var l = 1 # Number of elements in args
                args.append(command)
@@ -102,7 +104,6 @@ class Process
                if arguments != null then
                        for a in arguments do
                                args.add('\0')
-                               #a.output_class_name
                                args.append(a)
                        end
                        l += arguments.length
@@ -292,7 +293,7 @@ class ProcessDuplex
        # ~~~
        fun write_and_read(input: Text): String
        do
-               var read = new Buffer #new Array[String]
+               var read = new Buffer
 
                # Main loop, read and write line by line
                var prev = 0
@@ -321,7 +322,7 @@ end
 
 redef class Sys
        # Execute a shell command and return its error code
-       fun system(command: String): Int
+       fun system(command: Text): Int
        do
                return command.to_cstring.system
        end
index 48c7e12..d6ea633 100644 (file)
@@ -523,3 +523,15 @@ redef class UITableView
                self.dataSource = objc_delegate;
        `}
 end
+
+redef class Text
+       redef fun open_in_browser do to_nsstring.native_open_in_browser
+end
+
+redef class NSString
+       private fun native_open_in_browser
+       in "ObjC" `{
+               NSURL *nsurl = [NSURL URLWithString: self];
+               [[UIApplication sharedApplication] openURL: nsurl];
+       `}
+end
index 1e26f17..2c0eec1 100644 (file)
@@ -343,3 +343,7 @@ redef class TextInput
                super
        end
 end
+
+redef class Text
+       redef fun open_in_browser do system("xdg-open '{self.escape_to_sh}' &")
+end