From 2325d3e4df8cc0e2648f0f0cce36c6851fa64eec Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Wed, 20 Apr 2016 15:21:28 +0300 Subject: [PATCH] Android support refactoring; #119 --- examples/android/android_app.mk | 1 + examples/android/jni/app.d | 77 +++++++++++ examples/android/jni/main.d | 162 +++++++++++++----------- src/dlangui/platforms/common/platform.d | 9 +- 4 files changed, 169 insertions(+), 80 deletions(-) create mode 100644 examples/android/jni/app.d diff --git a/examples/android/android_app.mk b/examples/android/android_app.mk index bd782455..a69be166 100644 --- a/examples/android/android_app.mk +++ b/examples/android/android_app.mk @@ -6,6 +6,7 @@ LOCAL_MODULE=dlangui-activity # applicatino source files LOCAL_SRC_FILES="\ jni/main.d \ +jni/app.d \ " # Additional libraries to link diff --git a/examples/android/jni/app.d b/examples/android/jni/app.d new file mode 100644 index 00000000..d6a15fa9 --- /dev/null +++ b/examples/android/jni/app.d @@ -0,0 +1,77 @@ +module app; + +import dlangui; + +mixin APP_ENTRY_POINT; + +/// entry point for dlangui based application +extern (C) int UIAppMain(string[] args) { + // create window + Log.d("Creating window"); + if (!Platform.instance) { + Log.e("Platform.instance is null!!!"); + } + Window window = Platform.instance.createWindow("DlangUI example - HelloWorld", null); + Log.d("Window created"); + + // create some widget to show in window + //window.mainWidget = (new Button()).text("Hello, world!"d).margins(Rect(20,20,20,20)); + window.mainWidget = parseML(q{ + VerticalLayout { + margins: 10 + padding: 10 + layoutWidth: fill + backgroundColor: "#C0E0E070" // semitransparent yellow background + // red bold text with size = 150% of base style size and font face Arial + TextWidget { text: "Hello World example for DlangUI"; textColor: "red"; fontSize: 150%; fontWeight: 800; fontFace: "Arial" } + // arrange controls as form - table with two columns + TableLayout { + colCount: 2 + layoutWidth: fill + TextWidget { text: "param 1" } + EditLine { id: edit1; text: "some text"; layoutWidth: fill } + TextWidget { text: "param 2" } + EditLine { id: edit2; text: "some text for param2"; layoutWidth: fill } + TextWidget { text: "some radio buttons" } + // arrange some radio buttons vertically + VerticalLayout { + layoutWidth: fill + RadioButton { id: rb1; text: "Item 1" } + RadioButton { id: rb2; text: "Item 2" } + RadioButton { id: rb3; text: "Item 3" } + } + TextWidget { text: "and checkboxes" } + // arrange some checkboxes horizontally + HorizontalLayout { + layoutWidth: fill + CheckBox { id: cb1; text: "checkbox 1" } + CheckBox { id: cb2; text: "checkbox 2" } + } + } + HorizontalLayout { + Button { id: btnOk; text: "Ok" } + Button { id: btnCancel; text: "Cancel" } + } + } + }); + // you can access loaded items by id - e.g. to assign signal listeners + auto edit1 = window.mainWidget.childById!EditLine("edit1"); + auto edit2 = window.mainWidget.childById!EditLine("edit2"); + // close window on Cancel button click + window.mainWidget.childById!Button("btnCancel").click = delegate(Widget w) { + window.close(); + return true; + }; + // show message box with content of editors + window.mainWidget.childById!Button("btnOk").click = delegate(Widget w) { + window.showMessageBox(UIString("Ok button pressed"d), + UIString("Editors content\nEdit1: "d ~ edit1.text ~ "\nEdit2: "d ~ edit2.text)); + return true; + }; + + // show window + window.show(); + + // run message loop + return Platform.instance.enterMessageLoop(); +} diff --git a/examples/android/jni/main.d b/examples/android/jni/main.d index e673fddb..8ddf710a 100644 --- a/examples/android/jni/main.d +++ b/examples/android/jni/main.d @@ -92,10 +92,34 @@ class AndroidPlatform : Platform { protected AndroidWindow[] _windows; protected AndroidWindow _activeWindow; + engine _engine; + protected android_app* _appstate; this(android_app* state) { _appstate = state; + memset(&_engine, 0, engine.sizeof); + state.userData = &_engine; + state.onAppCmd = &engine_handle_cmd; + state.onInputEvent = &engine_handle_input; + _engine.app = state; + + // Prepare to monitor accelerometer + _engine.sensorManager = ASensorManager_getInstance(); + _engine.accelerometerSensor = ASensorManager_getDefaultSensor(_engine.sensorManager, + ASENSOR_TYPE_ACCELEROMETER); + _engine.sensorEventQueue = ASensorManager_createEventQueue(_engine.sensorManager, + state.looper, LOOPER_ID_USER, null, null); + + if (state.savedState != null) { + // We are starting with a previous saved state; restore from it. + _engine.state = *cast(saved_state*)state.savedState; + } + + } + + ~this() { + engine_term_display(&_engine); } /** @@ -138,8 +162,54 @@ class AndroidPlatform : Platform { * When returned from this method, application is shutting down. */ override int enterMessageLoop() { - // TODO: - return 0; + while (1) { + // Read all pending events. + int ident; + int events; + android_poll_source* source; + + // If not animating, we will block forever waiting for events. + // If animating, we loop until all events are read, then continue + // to draw the next frame of animation. + while ((ident=ALooper_pollAll(_engine.animating ? 0 : -1, null, &events, + cast(void**)&source)) >= 0) { + + // Process this event. + if (source != null) { + source.process(_appstate, source); + } + + // If a sensor has data, process it now. + if (ident == LOOPER_ID_USER) { + if (_engine.accelerometerSensor != null) { + ASensorEvent event; + while (ASensorEventQueue_getEvents(_engine.sensorEventQueue, + &event, 1) > 0) { + LOGI("accelerometer: x=%f y=%f z=%f", + event.acceleration.x, event.acceleration.y, + event.acceleration.z); + } + } + } + + // Check if we are exiting. + if (_appstate.destroyRequested != 0) { + return 0; + } + } + + if (_engine.animating) { + // Done with events; draw next animation frame. + _engine.state.angle += .01f; + if (_engine.state.angle > 1) { + _engine.state.angle = 0; + } + + // Drawing is throttled to the screen update rate, so there + // is no need to do timing here. + engine_draw_frame(&_engine); + } + } } protected dstring _clipboardText; @@ -393,86 +463,24 @@ extern (C) void android_main(android_app* state) { Platform.setInstance(_platform); - engine engine; - // Make sure glue isn't stripped. app_dummy(); - memset(&engine, 0, engine.sizeof); - state.userData = &engine; - state.onAppCmd = &engine_handle_cmd; - state.onInputEvent = &engine_handle_input; - engine.app = state; - - // Prepare to monitor accelerometer - engine.sensorManager = ASensorManager_getInstance(); - engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, - ASENSOR_TYPE_ACCELEROMETER); - engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, - state.looper, LOOPER_ID_USER, null, null); - - if (state.savedState != null) { - // We are starting with a previous saved state; restore from it. - engine.state = *cast(saved_state*)state.savedState; - } + int res = 0; + + version (unittest) { + } else { + res = UIAppMain([]); + } // loop waiting for stuff to do. + Log.d("Destroying Android platform"); + Platform.setInstance(null); + + releaseResourcesOnAppExit(); + + Log.d("Exiting main"); + - while (1) { - // Read all pending events. - int ident; - int events; - android_poll_source* source; - - // If not animating, we will block forever waiting for events. - // If animating, we loop until all events are read, then continue - // to draw the next frame of animation. - while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, null, &events, - cast(void**)&source)) >= 0) { - - // Process this event. - if (source != null) { - source.process(state, source); - } - - // If a sensor has data, process it now. - if (ident == LOOPER_ID_USER) { - if (engine.accelerometerSensor != null) { - ASensorEvent event; - while (ASensorEventQueue_getEvents(engine.sensorEventQueue, - &event, 1) > 0) { - LOGI("accelerometer: x=%f y=%f z=%f", - event.acceleration.x, event.acceleration.y, - event.acceleration.z); - } - } - } - - // Check if we are exiting. - if (state.destroyRequested != 0) { - Log.d("Destroying Android platform"); - Platform.setInstance(null); - - releaseResourcesOnAppExit(); - - Log.d("Exiting main"); - - engine_term_display(&engine); - return; - } - } - - if (engine.animating) { - // Done with events; draw next animation frame. - engine.state.angle += .01f; - if (engine.state.angle > 1) { - engine.state.angle = 0; - } - - // Drawing is throttled to the screen update rate, so there - // is no need to do timing here. - engine_draw_frame(&engine); - } - } } diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 7056f86a..d2ea9173 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -1440,9 +1440,12 @@ mixin template APP_ENTRY_POINT() { } } } else { - int main(string[] args) - { - return DLANGUImain(args); + version (Android) { + } else { + int main(string[] args) + { + return DLANGUImain(args); + } } } }