From: InHong Han Date: Thu, 12 Jan 2023 09:27:19 +0000 (+0900) Subject: Support Chinese/Japanese text input X-Git-Tag: accepted/tizen/unified/riscv/20230725.120108~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f2c63abf9634682c91e285567096d22ca32411a7;p=platform%2Fcore%2Fuifw%2Fise-default-nui.git Support Chinese/Japanese text input Change-Id: Id3966ce754c22d5301435f210d0f2da9d0d55c0d --- diff --git a/ISEDefaultNUI/CandidateView.cs b/ISEDefaultNUI/CandidateView.cs new file mode 100644 index 0000000..37c0975 --- /dev/null +++ b/ISEDefaultNUI/CandidateView.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections.Generic; +using Tizen; +using Tizen.Applications; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.NUI.Components; +using static Interop.SclNuiCSharp; + +namespace ISEDefaultNUI +{ + public class CandidateView + { + private static CandidateView instance; + private Timer timer; + private View parentView; + private ScrollableBase candidateView; + private bool visible; + private bool candidateExpanded; + private List candidateStrings; + private List labelList; + private List popupLineList; + private Button moreButton; + private Size imeSize; + private string selectedCandidate; + private int index = 0; + private int pointSize = 15; + private int itemWidth = 120; + private int viewHeight = 84; + private int moreButtonWidth = 89; + private int moreButtonHeight = 64; + private ushort margin = 5; + private static string ImageURL = Application.Current.DirectoryInfo.Resource + "image/soft_candidate/"; + + private CandidateView() + { + timer = new Timer(ResourceManager.CandidateHideTimerInterval); + timer.Tick += TimerTick; + + candidateView = new ScrollableBase() + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + BackgroundColor = Color.Black, + Padding = new Extents(margin, margin, margin, margin), + }; + + candidateStrings = new List(); + labelList = new List(); + popupLineList = new List(); + visible = false; + candidateExpanded = false; + } + + public static CandidateView Instance + { + get + { + if (instance == null) + instance = new CandidateView(); + + return instance; + } + } + + public List CandidateStrings + { + get => candidateStrings; + set + { + if (value == null) + throw new ArgumentException("Invalid parameter"); + + candidateStrings = value; + } + } + + public bool IsVisible + { + get => visible; + } + + public int ViewHeight + { + get => viewHeight; + } + + private bool TimerTick(object source, Timer.TickEventArgs e) + { + Log.Info("NUIIME", "Hide candidate view"); + parentView.Remove(candidateView); + visible = false; + candidateExpanded = false; + return false; + } + + private bool NeedEngineLoader() + { + bool loadInIme = false; + string currentLanguage = LanguageManager.Instance.GetCurrentLanguage(); + if (currentLanguage != null) + { + LanguageInfo languageInfo = LanguageManager.Instance.GetLanguageInfo(currentLanguage); + if (languageInfo != null) + loadInIme = languageInfo.LoadInIME; + } + + return !loadInIme; + } + + private bool OnTouchEvent(object source, View.TouchEventArgs e) + { + TextLabel candidate = source as TextLabel; + if (e.Touch.GetState(0) == PointStateType.Down) + { + selectedCandidate = candidate.Text; + } + else if (e.Touch.GetState(0) == PointStateType.Up) + { + if (candidate.Text.Equals(selectedCandidate)) + { + string candidateString = candidate.Text; + uint idx = (uint)candidateStrings.IndexOf(candidateString); + + if (NeedEngineLoader()) + { + SclDbusSelectCandidate(idx); + } + else + { + SclSelectCandidate(idx); + } + } + selectedCandidate = null; + } + + return true; + } + + private void CreateCandidateText(string text) + { + PropertyMap candidateStyle = new PropertyMap(); + candidateStyle.Add("weight", new PropertyValue("semibold")); + candidateStyle.Add("slant", new PropertyValue("dejavu")); + + TextLabel candidate = new TextLabel() + { + Margin = new Extents(0, margin, margin, margin), + Text = text, + PointSize = pointSize, + FontStyle = candidateStyle, + TextColor = Color.White, + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center, + Size = new Size(itemWidth, viewHeight - (margin * 3)), + BackgroundColor = Color.Black, + }; + + candidate.TouchEvent += OnTouchEvent; + candidateView.Add(candidate); + labelList.Add(candidate); + } + + private void moreButtonClicked(object sender, ClickedEventArgs e) + { + if (candidateExpanded) + { + candidateView.Size = new Size(imeSize.Width, viewHeight); + candidateExpanded = false; + } + else + { + candidateView.Size = new Size(imeSize.Width, imeSize.Height + viewHeight); + candidateExpanded = true; + + for (int i = index + 1; i < candidateStrings.Count; i++) + { + CreateCandidateText(candidateStrings[i]); + } + } + } + + private void CreateMoreButton() + { + ButtonStyle Style = new ButtonStyle + { + IsSelectable = true, + Size = new Size(moreButtonWidth, moreButtonHeight), + BackgroundColor = new Color(0.37f, 0.38f, 0.4f, 1.0f), + Icon = new ImageViewStyle + { + Size = new Size(moreButtonWidth / 2, moreButtonHeight / 2), + ResourceUrl = new Selector + { + Normal = ImageURL + "B09_predictive_icon_arrow_down.png", + Selected = ImageURL + "B09_predictive_icon_arrow_up.png", + }, + }, + }; + + moreButton = new Button(Style) + { + Margin = new Extents((ushort)(margin * 3), 0, (ushort)((viewHeight - moreButtonHeight) / 4), (ushort)((viewHeight - moreButtonHeight) / 4)), + Position = new Position(imeSize.Width - moreButtonWidth - ((viewHeight - moreButtonHeight) / 4), 0), + CellHorizontalAlignment = HorizontalAlignmentType.Right, + CellVerticalAlignment = VerticalAlignmentType.Center, + }; + + moreButton.Clicked += moreButtonClicked; + candidateView.Add(moreButton); + } + + private void ResetCandidateView() + { + if (labelList.Count > 0) + { + labelList.ForEach(label => + { + label.Unparent(); + label.Dispose(); + }); + } + + if (popupLineList.Count > 0) + { + popupLineList.ForEach(line => + { + line.Unparent(); + line.Dispose(); + }); + } + + labelList.Clear(); + popupLineList.Clear(); + + if (candidateExpanded) + { + candidateView.Size = new Size(imeSize.Width, viewHeight); + candidateExpanded = false; + } + + if (moreButton != null) + { + moreButton.Unparent(); + moreButton.Dispose(); + } + } + + public void UpdateCandidateList() + { + if (imeSize == null) + return; + + ResetCandidateView(); + index = 0; + int columnCount = (int)(imeSize.Width / (itemWidth + margin)); + GridLayout gridLayout = new GridLayout() + { + Columns = columnCount, + }; + candidateView.Layout = gridLayout; + + for (int i = 0; i < candidateStrings.Count; i++) + { + CreateCandidateText(candidateStrings[i]); + + if (i == columnCount - 2) + { + index = i; + CreateMoreButton(); + break; + } + } + } + + public void Show(View parent) + { + if (!visible) + { + Log.Info("NUIIME", "Show candidate view"); + parentView = parent; + parent.Add(candidateView); + visible = true; + } + } + + public void Hide() + { + if (timer.IsRunning()) + { + timer.Stop(); + } + + timer.Start(); + } + + public void SetPosition(Size imeSize) + { + this.imeSize = imeSize; + candidateView.Size = new Size(imeSize.Width, viewHeight); + candidateView.Position = new Position(0, 0); + } + } +} diff --git a/ISEDefaultNUI/Common/ResourceManager.cs b/ISEDefaultNUI/Common/ResourceManager.cs index d0001ca..ecfec06 100644 --- a/ISEDefaultNUI/Common/ResourceManager.cs +++ b/ISEDefaultNUI/Common/ResourceManager.cs @@ -20,6 +20,7 @@ public const int MVKDone = 0xff0d; public const int LayoutNumberOnlyVariationMax = 4; public const uint MagnifierShowDuration = 300; + public const uint CandidateHideTimerInterval = 100; /* FIXME : If an API is provided to get the screen size, use the API */ public const int screenWidth = 1920; diff --git a/ISEDefaultNUI/ISEDefaultNUI.cs b/ISEDefaultNUI/ISEDefaultNUI.cs index e53ccd2..cd7dd40 100644 --- a/ISEDefaultNUI/ISEDefaultNUI.cs +++ b/ISEDefaultNUI/ISEDefaultNUI.cs @@ -632,6 +632,10 @@ namespace ISEDefaultNUI if (keyboardState.CheckContextTemporary(keyboardState.Context) && !keyboardState.CheckContextTemporary(e.ContextId)) keyboardState.Context = e.ContextId; keyboardState.FocusedContext = e.ContextId; + + if (CandidateView.Instance.IsVisible) + CandidateView.Instance.Hide(); + SclDbus.FocusIn(); } diff --git a/ISEDefaultNUI/Interop/Interop.SclNui.cs b/ISEDefaultNUI/Interop/Interop.SclNui.cs index 97c50e6..063ffe3 100644 --- a/ISEDefaultNUI/Interop/Interop.SclNui.cs +++ b/ISEDefaultNUI/Interop/Interop.SclNui.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices; using Tizen; using Tizen.Internals; @@ -105,6 +106,12 @@ internal static partial class Interop Done /* We're done with this event, do not call any other default SCL event handlers */ } + internal enum SclNuiCandidateEvent + { + Start = 0, + End + } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] //internal delegate void DrawTextCallback(IntPtr str, int x, int y, int w, int h, int fontsize, IntPtr userData); //internal delegate void DrawTextCallback(SclFontInfo fontinfo, SclColor color, IntPtr str, int x, int y, int w, int h, int label_alignment, int padding_x, int padding_y, int inner_width, int inner_height, IntPtr userData); @@ -138,6 +145,18 @@ internal static partial class Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void ProcessKeyEventWithImengineCallback(UInt32 code, UInt32 mask, UInt32 layout, UInt32 devClass, UInt32 devSubclass, string devName, UInt32 serial, IntPtr user_data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void CandidateShowCallback(IntPtr user_data); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void CandidateHideCallback(IntPtr user_data); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void LookupTableChangedCallback(SclNuiCandidateEvent eventType, IntPtr user_data); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void CandidateForeachCallback(IntPtr candidate, IntPtr user_data); + [DllImport(Libraries.SclNui, EntryPoint = "scl_nui_init")] internal static extern int SclNuiInit(string entry_filepath); @@ -341,5 +360,23 @@ internal static partial class Interop [DllImport(Libraries.SclNui, EntryPoint = "scl_set_process_key_event_with_imengine_cb")] internal static extern int SclSetProcessKeyEventWithImengineCb(ProcessKeyEventWithImengineCallback callbackFunction, IntPtr userData); + + [DllImport(Libraries.SclNui, EntryPoint = "scl_set_candidate_show_cb")] + internal static extern int SclSetCandidateShowCb(CandidateShowCallback callbackFunction, IntPtr userData); + + [DllImport(Libraries.SclNui, EntryPoint = "scl_set_candidate_hide_cb")] + internal static extern int SclSetCandidateHideCb(CandidateHideCallback callbackFunction, IntPtr userData); + + [DllImport(Libraries.SclNui, EntryPoint = "scl_set_lookup_table_changed_cb")] + internal static extern int SclSetLookupTableChangedCb(LookupTableChangedCallback callbackFunction, IntPtr userData); + + [DllImport(Libraries.SclNui, EntryPoint = "scl_set_candidate_string_foreach_cb")] + internal static extern int SclSetCandidateStringForeachCb(CandidateForeachCallback callbackFunction, IntPtr userData); + + [DllImport(Libraries.SclNui, EntryPoint = "scl_nui_set_custom_starting_coordinates")] + internal static extern int SclNuiSetCustomStartingCoordinates(int x, int y); + + [DllImport(Libraries.SclNui, EntryPoint = "scl_select_candidate")] + internal static extern int SclSelectCandidate(UInt32 index); } } diff --git a/ISEDefaultNUI/SCLNUI.cs b/ISEDefaultNUI/SCLNUI.cs index ae01bad..5f33fca 100644 --- a/ISEDefaultNUI/SCLNUI.cs +++ b/ISEDefaultNUI/SCLNUI.cs @@ -23,6 +23,10 @@ namespace ISEDefaultNUI private EventNotificationCallback _onEventNotification = null; private UpdateWindowPositionCallback _updateWindowPositionChangedEvent = null; private ProcessKeyEventWithImengineCallback _onProcessKeyEventWithImengineEvent = null; + private CandidateShowCallback _onCandidateShowEvent = null; + private CandidateHideCallback _onCandidateHideEvent = null; + private LookupTableChangedCallback _onLookupTableChangedEvent = null; + private CandidateForeachCallback _onCandidateStringForeach = null; private List image_list; private List label_list; @@ -51,6 +55,7 @@ namespace ISEDefaultNUI private Size2D imePortraitSize; private Size2D imeLandscapeSize; + private CandidateView candidateView = CandidateView.Instance; enum LabelAlignment { @@ -79,6 +84,13 @@ namespace ISEDefaultNUI Max, } + enum CandidateRequest + { + None = 0, + Show, + Hide, + } + public SCLNUI(Navigator Navigator) : base() { Layout = new AbsoluteLayout(); @@ -548,6 +560,56 @@ namespace ISEDefaultNUI } }; SclNuiUpdateWindowPositionCb(_updateWindowPositionChangedEvent, (IntPtr)null); + + _onCandidateShowEvent = (IntPtr userData) => + { + if (imePortraitSize == null || imeLandscapeSize == null) + GetDefaultSize(); + + if (candidateView.IsVisible == false) + { + if (deviceAngle == 0 || deviceAngle == 180) + candidateView.SetPosition(imePortraitSize); + else + candidateView.SetPosition(imeLandscapeSize); + candidateView.Show(this); + SetImeSize(CandidateRequest.Show); + } + }; + SclSetCandidateShowCb(_onCandidateShowEvent, (IntPtr)null); + + _onCandidateHideEvent = (IntPtr userData) => + { + if (candidateView.IsVisible) + { + candidateView.Hide(); + } + SetImeSize(CandidateRequest.Hide); + }; + SclSetCandidateHideCb(_onCandidateHideEvent, (IntPtr)null); + + _onLookupTableChangedEvent = (SclNuiCandidateEvent eventType, IntPtr userData) => + { + switch (eventType) + { + case SclNuiCandidateEvent.Start: + candidateView.CandidateStrings.Clear(); + break; + case SclNuiCandidateEvent.End: + candidateView.UpdateCandidateList(); + break; + default: + break; + } + }; + SclSetLookupTableChangedCb(_onLookupTableChangedEvent, (IntPtr)null); + + _onCandidateStringForeach = (IntPtr candidate, IntPtr userData) => + { + string candidateString = Marshal.PtrToStringAnsi(candidate); + candidateView.CandidateStrings.Add(candidateString); + }; + SclSetCandidateStringForeachCb(_onCandidateStringForeach, (IntPtr)null); } private bool CommitTimerTick(object source, Timer.TickEventArgs e) @@ -590,7 +652,6 @@ namespace ISEDefaultNUI Log.Info("NUIIME", "### start draw text"); Log.Info("NUIIME", "font name(" + FontName + "), italic(" + is_italic + "), bold(" + is_bold + ")"); - Log.Info("NUIIME", "key string(" + keystr + "), x: " + pos_x + ", y: " + pos_y); Log.Info("NUIIME", "padding_x:" + padding_x + ", padding_y: " + padding_y + ", inner_width: " + inner_width + ", inner_height: " + inner_height); @@ -765,6 +826,49 @@ namespace ISEDefaultNUI emoticonView = null; } + private void SetImeSize(CandidateRequest request) + { + if (imePortraitSize == null || imeLandscapeSize == null) + GetDefaultSize(); + + switch (request) + { + case CandidateRequest.None: + if (candidateView.IsVisible) + SclNuiSetCustomStartingCoordinates(0, candidateView.ViewHeight); + else + SclNuiSetCustomStartingCoordinates(0, 0); + break; + case CandidateRequest.Show: + SclNuiSetCustomStartingCoordinates(0, candidateView.ViewHeight); + imePortraitSize = new Size2D(imePortraitSize.Width, imePortraitSize.Height + candidateView.ViewHeight); + imeLandscapeSize = new Size2D(imeLandscapeSize.Width, imeLandscapeSize.Height + candidateView.ViewHeight); + break; + case CandidateRequest.Hide: + SclNuiSetCustomStartingCoordinates(0, 0); + break; + default: + break; + } + + IntPtr nativeHandle = new Window.SafeNativeWindowHandle().DangerousGetHandle(); + InputMethodEditor.SetSize(nativeHandle, imePortraitSize.Width, imePortraitSize.Height, imeLandscapeSize.Width, imeLandscapeSize.Height); + } + + private void GetDefaultSize() + { + IntPtr inputModePtr; + GetInputMode(out inputModePtr); + string inputMode = Marshal.PtrToStringAnsi(inputModePtr); + + int portraitWidth, portraitHeight, landscapeWidth, landscapeHeight; + GetInputModeSize(inputMode, (int)WindowAttribute.DisplayMode.Portrait, out portraitWidth, out portraitHeight); + GetInputModeSize(inputMode, (int)WindowAttribute.DisplayMode.Landscape, out landscapeWidth, out landscapeHeight); + + imePortraitSize = new Size2D(portraitWidth, portraitHeight); + imeLandscapeSize = new Size2D(landscapeWidth, landscapeHeight); + } + public int Init(string entry_filepath) { return SclNuiInit(entry_filepath); diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_key_btn_01.png b/ISEDefaultNUI/res/image/soft_candidate/B09_key_btn_01.png new file mode 100755 index 0000000..70a8858 Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_key_btn_01.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_key_btn_press.png b/ISEDefaultNUI/res/image/soft_candidate/B09_key_btn_press.png new file mode 100755 index 0000000..400e110 Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_key_btn_press.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_horizon_line.png b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_horizon_line.png new file mode 100755 index 0000000..530ef14 Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_horizon_line.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_down.png b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_down.png new file mode 100755 index 0000000..c45721c Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_down.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_down_press.png b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_down_press.png new file mode 100755 index 0000000..976fb72 Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_down_press.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_up.png b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_up.png new file mode 100755 index 0000000..ceb1a2d Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_up.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_up_press.png b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_up_press.png new file mode 100755 index 0000000..7b787fc Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_icon_arrow_up_press.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_vertical_line.png b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_vertical_line.png new file mode 100755 index 0000000..73d18ea Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/B09_predictive_vertical_line.png differ diff --git a/ISEDefaultNUI/res/image/soft_candidate/textinput_qwerty_candidate_bg.#.png b/ISEDefaultNUI/res/image/soft_candidate/textinput_qwerty_candidate_bg.#.png new file mode 100644 index 0000000..8eac787 Binary files /dev/null and b/ISEDefaultNUI/res/image/soft_candidate/textinput_qwerty_candidate_bg.#.png differ