/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * 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. */ using System; using System.Runtime.InteropServices; using System.Collections.Generic; namespace ElmSharp { /// /// The GestureLayer is used to detect gestures. /// Inherits EvasObject /// public class GestureLayer : EvasObject { private readonly Interop.Elementary.GestureEventCallback _gestureCallback; // Important: don't remove items from _handlers list // The list can grow up to (number of GestureType) * (number of GestureState) // but all gestures share the callback and you don't want to desynchronize mapping private readonly List _handlers = new List(); /// /// Creates and initializes a new instance of GestureLayer class. /// /// The parent is a given container which will be attached by GestureLayer as a child. It's type. public GestureLayer(EvasObject parent) : base(parent) { _gestureCallback = new Interop.Elementary.GestureEventCallback(GestureCallbackHandler); } /// /// Enumeration for supported gesture types. /// public enum GestureType { /// /// N fingers single taps /// Tap = 1, /// /// N fingers single long-taps /// LongTap, /// /// N fingers double-single taps /// DoubleTap, /// /// N fingers triple-single taps /// TripleTap, /// /// Reports momentum in the direction of move /// Momentum, /// /// N fingers line gesture /// Line, /// /// N fingers flick gesture /// Flick, /// /// Zoom /// Zoom, /// /// Rotate /// Rotate, } /// /// Enumeration for gesture states. /// public enum GestureState { /// /// Gesture not started /// Undefined = -1, /// /// Gesture started /// Start, /// /// Gesture is ongoing /// Move, /// /// Gesture completed /// End, /// /// Ongoing gesture is aborted /// Abort, } #region Properties /// /// Sets or gets the repeat-events setting. /// public bool HoldEvents { get { return Interop.Elementary.elm_gesture_layer_hold_events_get(Handle); } set { Interop.Elementary.elm_gesture_layer_hold_events_set(Handle, value); } } /// /// Sets or gets the gesture layer continues enable of an object /// public bool Continues { get { return Interop.Elementary.elm_gesture_layer_continues_enable_get(Handle); } set { Interop.Elementary.elm_gesture_layer_continues_enable_set(Handle, value); } } /// /// Sets or gets the gesture layer finger-size for taps. /// public int TapFingerSize { get { return Interop.Elementary.elm_gesture_layer_tap_finger_size_get(Handle); } set { Interop.Elementary.elm_gesture_layer_tap_finger_size_set(Handle, value); } } /// /// Sets or gets the gesture layer long tap start timeout of an object /// public double LongTapTimeout { get { return Interop.Elementary.elm_gesture_layer_long_tap_start_timeout_get(Handle); } set { Interop.Elementary.elm_gesture_layer_long_tap_start_timeout_set(Handle, value); } } /// /// Sets or gets the gesture layer double tap timeout of an object /// public double DoubleTapTimeout { get { return Interop.Elementary.elm_gesture_layer_double_tap_timeout_get(Handle); } set { Interop.Elementary.elm_gesture_layer_double_tap_timeout_set(Handle, value); } } /// /// Sets or gets the gesture layer flick time limit (in ms) of an object /// public int FlickTimeLimit { get { return (int)Interop.Elementary.elm_gesture_layer_flick_time_limit_ms_get(Handle); } set { Interop.Elementary.elm_gesture_layer_flick_time_limit_ms_set(Handle, (UInt32)value); } } /// /// Sets or gets the gesture layer line min length of an object /// public int MinimumLineLength { get { return Interop.Elementary.elm_gesture_layer_line_min_length_get(Handle); } set { Interop.Elementary.elm_gesture_layer_line_min_length_set(Handle, value); } } /// /// Sets or gets the gesture layer line angular tolerance of an object /// public double LineAngularTolerance { get { return Interop.Elementary.elm_gesture_layer_line_angular_tolerance_get(Handle); } set { Interop.Elementary.elm_gesture_layer_line_angular_tolerance_set(Handle, value); } } /// /// Sets or gets the gesture layer line distance tolerance of an object /// public int LineDistanceTolerance { get { return Interop.Elementary.elm_gesture_layer_line_distance_tolerance_get(Handle); } set { Interop.Elementary.elm_gesture_layer_line_distance_tolerance_set(Handle, value); } } /// /// Sets or gets step-value for rotate action. /// public double RotateStep { get { return Interop.Elementary.elm_gesture_layer_rotate_step_get(Handle); } set { Interop.Elementary.elm_gesture_layer_rotate_step_set(Handle, value); } } /// /// Sets or gets the gesture layer rotate angular tolerance of an object /// public double RotateAngularTolerance { get { return Interop.Elementary.elm_gesture_layer_rotate_angular_tolerance_get(Handle); } set { Interop.Elementary.elm_gesture_layer_rotate_angular_tolerance_set(Handle, value); } } /// /// Sets or gets control step value for zoom action. /// public double ZoomStep { get { return Interop.Elementary.elm_gesture_layer_zoom_step_get(Handle); } set { Interop.Elementary.elm_gesture_layer_zoom_step_set(Handle, value); } } /// /// Sets or gets the gesture layer zoom distance tolerance of an object /// public int ZoomDistanceTolerance { get { return Interop.Elementary.elm_gesture_layer_zoom_distance_tolerance_get(Handle); } set { Interop.Elementary.elm_gesture_layer_zoom_distance_tolerance_set(Handle, value); } } /// /// Sets or gets the gesture layer zoom finger factor of an object /// public double ZoomFingerFactor { get { return Interop.Elementary.elm_gesture_layer_zoom_finger_factor_get(Handle); } set { Interop.Elementary.elm_gesture_layer_zoom_finger_factor_set(Handle, value); } } /// /// Sets or gets the gesture layer zoom wheel factor of an object /// public double ZoomWheelFactor { get { return Interop.Elementary.elm_gesture_layer_zoom_wheel_factor_get(Handle); } set { Interop.Elementary.elm_gesture_layer_zoom_wheel_factor_set(Handle, value); } } #endregion Properties /// /// Attach a gesture layer widget to an Evas object (setting the widget's target). /// A gesture layer's target may be any Evas object. This object will be used to listen to mouse and key events. /// /// The object to attach. public void Attach(EvasObject target) { Interop.Elementary.elm_gesture_layer_attach(Handle, target.Handle); } /// /// Set the gesture state change callback. /// When all callbacks for the gesture are set to null, it means this gesture is disabled. /// /// The gesture you want to track state of. /// The event the callback tracks (START, MOVE, END, ABORT). /// The callback itself. public void SetGestureCallback(GestureType type, GestureState state, Action action) { lock (_handlers) { bool found = false; int i = 0; // if this (type, state) already exists in _handlers, we will reuse it foreach (var handler in _handlers) { if (handler.Type == type && handler.State == state) { found = true; break; } i++; } if (found) { // if we are changing null -> not-null, or not-null -> null, then inform the EFL if (_handlers[i].Action == null ^ action == null) Interop.Elementary.elm_gesture_layer_cb_set(Handle, type, state, action == null ? null : _gestureCallback, new IntPtr(i)); // overwrite previous action _handlers[i].Action = action; } else { if (action == null) { // ignore unsetting a handler for event which was not registered yet? return; } // (type, state) was not found, so we are adding a new entry and registering the callback _handlers.Add(new NativeCallback(type, state, action)); // callback is always the same, the event is recognised by the index in _handler list (the index is passed as data) Interop.Elementary.elm_gesture_layer_cb_set(Handle, type, state, _gestureCallback, new IntPtr(i)); } } } /// /// clear the gesture state change callback. /// public void ClearCallbacks() { lock (_handlers) { int i = 0; foreach (var handler in _handlers) { if (handler.Action != null) { Interop.Elementary.elm_gesture_layer_cb_set(Handle, handler.Type, handler.State, null, new IntPtr(i)); handler.Action = null; } i++; } } } #region Typed callback setting methods // Following methods have been added for convenience, so the user will not have to convert Info structures himself /// /// Set the tap callback. /// /// The gesture you want to track state of. /// The event the callback tracks (START, MOVE, END, ABORT). /// The callback itself. public void SetTapCallback(GestureType type, GestureState state, Action action) { SetCallback(type, state, action); } /// /// Set the gesture state change callback with Momentum Gesture Type /// /// The event the callback tracks (START, MOVE, END, ABORT). /// The callback itself. public void SetMomentumCallback(GestureState state, Action action) { SetCallback(GestureType.Momentum, state, action); } /// /// Set the gesture state change callback with Line Gesture Type /// /// The event the callback tracks (START, MOVE, END, ABORT). /// The callback itself. public void SetLineCallback(GestureState state, Action action) { SetCallback(GestureType.Line, state, action); } /// /// Set the gesture state change callback with Flick Gesture Type /// /// The event the callback tracks (START, MOVE, END, ABORT). /// The callback itself. public void SetFlickCallback(GestureState state, Action action) { SetCallback(GestureType.Flick, state, action); } /// /// Set the gesture state change callback with Zoom Gesture Type /// /// The event the callback tracks (START, MOVE, END, ABORT). /// The callback itself. public void SetZoomCallback(GestureState state, Action action) { SetCallback(GestureType.Zoom, state, action); } /// /// Set the gesture state change callback with Rotate Gesture Type /// /// The event the callback tracks (START, MOVE, END, ABORT). /// The callback itself. public void SetRotateCallback(GestureState state, Action action) { SetCallback(GestureType.Rotate, state, action); } #endregion Typed callback setting methods /// /// Call this function to construct a new gesture-layer object. /// /// The gesture layer's parent widget. /// protected override IntPtr CreateHandle(EvasObject parent) { return Interop.Elementary.elm_gesture_layer_add(parent); } /// /// clear the gesture state change callback. /// protected override void OnUnrealize() { ClearCallbacks(); base.OnUnrealize(); } private void SetCallback(GestureType type, GestureState state, Action action) { if (action == null) SetGestureCallback(type, state, null); else SetGestureCallback(type, state, new Action((info) => action((T)info))); } private void GestureCallbackHandler(IntPtr data, IntPtr event_info) { // so EFL called our callback, lets use data to find the right Action to call var handlerIndex = (int)data; // thanks to the fact that we never remove item from _handlers, we don't need a lock here if (handlerIndex < 0 || handlerIndex >= _handlers.Count) return; Action action = _handlers[handlerIndex].Action; if (action == null) return; // the interpretation of the event_info struct pointer depends on the GestureType switch (_handlers[handlerIndex].Type) { case GestureType.Tap: case GestureType.LongTap: case GestureType.DoubleTap: case GestureType.TripleTap: action(Marshal.PtrToStructure(event_info)); break; case GestureType.Momentum: action(Marshal.PtrToStructure(event_info)); break; case GestureType.Line: case GestureType.Flick: action(Marshal.PtrToStructure(event_info)); break; case GestureType.Zoom: action(Marshal.PtrToStructure(event_info)); break; case GestureType.Rotate: action(Marshal.PtrToStructure(event_info)); break; } } #region Info structures /// /// The struct of TapData /// [StructLayout(LayoutKind.Sequential)] public struct TapData { /// /// The x coordinate of the center point. /// public Int32 X; /// /// The y coordinate of the center point. /// public Int32 Y; #pragma warning disable 3003 /// /// The number of fingers tapped. /// public UInt32 FingersCount; /// /// The timestamp. /// public UInt32 Timestamp; #pragma warning restore 3003 } /// /// The struct of MomentumData /// [StructLayout(LayoutKind.Sequential)] public struct MomentumData { /// /// Final-swipe direction starting point on X. /// public Int32 X1; /// /// Final-swipe direction starting point on Y. /// public Int32 Y1; /// /// Final-swipe direction ending point on X. /// public Int32 X2; /// /// Final-swipe direction ending point on Y /// public Int32 Y2; #pragma warning disable 3003 /// /// Timestamp of start of final x-swipe. /// public UInt32 HorizontalSwipeTimestamp; /// /// Timestamp of start of final y-swipe. /// public UInt32 VerticalSwipeTimestamp; /// /// Momentum on X. /// public Int32 HorizontalMomentum; /// /// Momentum on Y. /// public Int32 VerticalMomentum; /// /// Number of fingers. /// public UInt32 FingersCount; #pragma warning restore 3003 } /// /// The struct of LineData /// [StructLayout(LayoutKind.Sequential)] public struct LineData { /// /// Final-swipe direction starting point on X. /// public Int32 X1; /// /// Final-swipe direction starting point on Y. /// public Int32 Y1; /// /// Final-swipe direction ending point on X. /// public Int32 X2; /// /// Final-swipe direction ending point on Y /// public Int32 Y2; #pragma warning disable 3003 /// /// Timestamp of start of final x-swipe. /// public UInt32 HorizontalSwipeTimestamp; /// /// Timestamp of start of final y-swipe. /// public UInt32 VerticalSwipeTimestamp; /// /// Momentum on X. /// public Int32 HorizontalMomentum; /// /// Momentum on Y. /// public Int32 VerticalMomentum; /// /// Number of fingers. /// public UInt32 FingersCount; #pragma warning restore 3003 /// /// Angle (direction) of lines. /// public double Angle; } /// /// The struct of ZoomData /// [StructLayout(LayoutKind.Sequential)] public struct ZoomData { /// /// The x coordinate of zoom center point reported to user. /// public Int32 X; /// /// The y coordinate of zoom center point reported to user. /// public Int32 Y; /// /// The radius (distance) between fingers reported to user. /// public Int32 Radius; /// /// The zoom value. 1.0 means no zoom. /// public double Zoom; /// /// Zoom momentum: zoom growth per second (NOT YET SUPPORTED). /// private double Momentum; } /// /// The struct of RotateData /// [StructLayout(LayoutKind.Sequential)] public struct RotateData { /// /// The x coordinate of rotation center point reported to user. /// public Int32 X; /// /// The y coordinate of rotation center point reported to user. /// public Int32 Y; /// /// The radius (distance) between fingers reported to user. /// public Int32 Radius; /// /// The start-angle. /// public double BaseAngle; /// /// The rotation value. 0.0 means no rotation. /// public double Angle; /// /// Rotation momentum: rotation done per second (NOT YET SUPPORTED). /// private double Momentum; } #endregion Info structures /// /// Config is a static class, it provides gestureLayer's timeout information. /// public static class Config { /// /// Sets or gets the duration for occurring long tap event of gesture layer. /// public static double DefaultLongTapTimeout { get { return Interop.Elementary.elm_config_glayer_long_tap_start_timeout_get(); } set { Interop.Elementary.elm_config_glayer_long_tap_start_timeout_set(value); } } /// /// Sets or gets the duration for occurring double tap event of gesture layer. /// public static double DefaultDoubleTapTimeout { get { return Interop.Elementary.elm_config_glayer_double_tap_timeout_get(); } set { Interop.Elementary.elm_config_glayer_double_tap_timeout_set(value); } } } private class NativeCallback { public readonly GestureType Type; public readonly GestureState State; public Action Action; public NativeCallback(GestureType type, GestureState state, Action action) { Type = type; State = state; Action = action; } } } }