diff --git a/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas b/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas index 2571d7ad46..b4d49f879f 100644 --- a/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas +++ b/windows/src/developer/TIKE/child/UfrmKeymanWizard.pas @@ -653,6 +653,8 @@ begin GetCharMapDropTool.Handle(Self, cmimText); GetCharMapDropTool.Handle(editKeyOutputCode, cmimCode); frameSource.SetupCharMapDrop; + frameTouchLayout.SetupCharMapDrop; + frameTouchLayoutSource.SetupCharMapDrop; FillFeatureGrid; @@ -3144,7 +3146,7 @@ begin if pagesTouchLayout.ActivePage = pageTouchLayoutCode then begin frameTouchLayoutSource.EditorText := frameTouchLayout.SaveToString; - DoFocus(frameTouchLayout); + DoFocus(frameTouchLayoutSource); end; FLoading := False; end; diff --git a/windows/src/developer/TIKE/main/UframeTextEditor.pas b/windows/src/developer/TIKE/main/UframeTextEditor.pas index 3249ba38c7..e59637164c 100644 --- a/windows/src/developer/TIKE/main/UframeTextEditor.pas +++ b/windows/src/developer/TIKE/main/UframeTextEditor.pas @@ -60,6 +60,7 @@ type cef: TframeCEFHost; FFilename: string; FOnBreakpointClicked: TEditorBreakpointClickedEvent; + FErrorLine: Integer; procedure RefreshOptions; function GetText: WideString; @@ -288,7 +289,10 @@ procedure TframeTextEditor.cefPreKeySyncEvent(Sender: TObject; e: TCEFHostKeyEventData; out isShortcut, Handled: Boolean); begin AssertCefThread; - if e.event.windows_key_code = VK_ESCAPE then + Handled := False; + // While FErrorLine is from main thread, this is unlikely to cause trouble so + // will not worry about synchronisation + if (e.event.windows_key_code = VK_ESCAPE) and (FErrorLine > 0) then Handled := True; end; @@ -926,6 +930,9 @@ end; procedure TframeTextEditor.FindError(ln: Integer); begin ClearError; + + FErrorLine := ln; + if (ln <= 0) then Exit; ExecuteLineCommand(ln, 'highlightError'); diff --git a/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas b/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas index 9564251883..76a55cba43 100644 --- a/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas +++ b/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas @@ -1,18 +1,18 @@ (* Name: UframeTouchLayoutBuilder Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 21 Aug 2013 Modified Date: 3 Aug 2015 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 21 Aug 2013 - mcdurdin - I3894 - V9.0 - Support modified state for touch layout builder 21 Aug 2013 - mcdurdin - I3895 - V9.0 - App hotkeys don't work within layout builder 07 Nov 2013 - mcdurdin - I3945 - V9.0 - Touch Layout Editor should allow import from existing On Screen Keyboard @@ -36,9 +36,24 @@ unit UframeTouchLayoutBuilder; interface uses - Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, - Vcl.Controls, Vcl.Forms, Vcl.Dialogs, UfrmTike, UserMessages, KMDActionInterfaces, - KeyboardFonts, TempFileManager, Keyman.Developer.UI.UframeCEFHost; + System.Classes, + System.JSON, + System.SysUtils, + System.Variants, + Winapi.Messages, + Winapi.Windows, + Vcl.Controls, + Vcl.Dialogs, + Vcl.Forms, + Vcl.Graphics, + + Keyman.Developer.UI.UframeCEFHost, + + KeyboardFonts, + KMDActionInterfaces, + TempFileManager, + UfrmTike, + UserMessages; type TframeTouchLayoutBuilder = class(TTikeForm, IKMDEditActions) @@ -62,19 +77,25 @@ type FLastError: string; // I4083 FLastErrorOffset: Integer; // I4083 FFilename: string; + FSourceWasRegistered: Boolean; function GetLayoutJS: string; procedure DoModified; procedure DoLoad; procedure FireCommand(const commands: TStringList); virtual; procedure DoSelectTemplate; procedure DoImportFromOSK; - function BuilderCommand(const cmd: string): Boolean; + function BuilderCommand(const cmd: string; const parameters: TJSONValue = nil): Boolean; procedure UpdateCharacterMap(code: string); // I4046 function GetFontInfo(Index: TKeyboardFont): TKeyboardFontInfo; // I4057 procedure SetFontInfo(Index: TKeyboardFont; const Value: TKeyboardFontInfo); // I4057 procedure cefBeforeBrowse(Sender: TObject; const Url: string; params: TStringList; wasHandled: Boolean); procedure cefLoadEnd(Sender: TObject); + procedure RegisterSource; + procedure UnregisterSource; + procedure CharMapDragDrop(Sender, Source: TObject; X, Y: Integer); + procedure CharMapDragOver(Sender, Source: TObject; X, Y: Integer; + State: TDragState; var Accept: Boolean); protected function GetHelpTopic: string; override; @@ -95,6 +116,7 @@ type public { Public declarations } + procedure SetupCharMapDrop; function Load(const AFilename: string; ALoadFromTemplate, ALoadFromString: Boolean): Boolean; procedure Save(const AFilename: string); function SaveToString: string; @@ -112,7 +134,7 @@ type implementation uses - System.JSON, + System.TypInfo, xmlintf, xmldoc, @@ -120,6 +142,9 @@ uses Keyman.Developer.System.HelpTopics, Keyman.Developer.System.TouchLayoutToVisualKeyboardConverter, + CharacterDragObject, + CharMapDropTool, + CharMapInsertMode, KeymanDeveloperOptions, VKeys, OnScreenKeyboardData, @@ -196,13 +221,29 @@ begin cef.Visible := True; cef.OnBeforeBrowse := cefBeforeBrowse; cef.OnLoadEnd := cefLoadEnd; + SetupCharMapDrop; end; procedure TframeTouchLayoutBuilder.FormDestroy(Sender: TObject); begin inherited; + UnregisterSource; +end; + +procedure TframeTouchLayoutBuilder.RegisterSource; +begin if FFilename <> '' then + modWebHttpServer.AppSource.RegisterSource(FFilename, FSavedLayoutJS); + + FSourceWasRegistered := True; +end; + +procedure TframeTouchLayoutBuilder.UnregisterSource; +begin + if (FFilename <> '') and FSourceWasRegistered then modWebHttpServer.AppSource.UnregisterSource(FFilename); + + FSourceWasRegistered := False; end; procedure TframeTouchLayoutBuilder.ImportFromKVK(const KVKFileName: string); // I3945 @@ -288,6 +329,8 @@ begin // FreeAndNil(FHTMLTempFilename); // I4195 // FHTMLTempFilename := TTempFileManager.Get('.html'); // I4195 + UnregisterSource; + if ALoadFromString then begin FNewLayoutJS := AFilename; @@ -344,7 +387,7 @@ begin FTouchLayout.Free; end; - modWebHttpServer.AppSource.RegisterSource(FFilename, FSavedLayoutJS); + RegisterSource; try DoLoad; @@ -474,14 +517,21 @@ begin FLoading := False; end; -function TframeTouchLayoutBuilder.BuilderCommand(const cmd: string): Boolean; +function TframeTouchLayoutBuilder.BuilderCommand(const cmd: string; const parameters: TJSONValue): Boolean; begin Result := False; if FLoading then Exit; // I4057 FDisplayScriptErrors := False; // I4047 try try - cef.cef.ExecuteJavaScript('builder.'+cmd+'();', ''); + if Assigned(parameters) then + begin + cef.cef.ExecuteJavaScript('builder.'+cmd+'('+parameters.ToJSON+')', ''); + end + else + begin + cef.cef.ExecuteJavaScript('builder.'+cmd+'();', ''); + end; except // Ignore errors end; @@ -588,4 +638,50 @@ begin BuilderCommand('redo'); end; + +procedure TframeTouchLayoutBuilder.CharMapDragOver(Sender, Source: TObject; X, Y: Integer; + State: TDragState; var Accept: Boolean); +var + j: TJSONObject; +begin + cef.cefwp.SetFocus; + j := TJSONObject.Create; + try + j.AddPair('x', TJSONNumber.Create(X)); + j.AddPair('y', TJSONNumber.Create(Y)); + j.AddPair('state', GetEnumName(TypeInfo(TDragState), Ord(State))); + BuilderCommand('charmapDragOver', j); + finally + j.Free; + end; + + // We cannot test acceptance via event because it is asynchronous + // So we will just assume we can accept and throw it away if it is outside bounds + // during drop. + Accept := True; +end; + +procedure TframeTouchLayoutBuilder.CharMapDragDrop(Sender, Source: TObject; X, Y: Integer); +var + j: TJSONObject; + cdo: TCharacterDragObject; +begin + cef.cefwp.SetFocus; + cdo := Source as TCharacterDragObject; + j := TJSONObject.Create; + try + j.AddPair('x', TJSONNumber.Create(X)); + j.AddPair('y', TJSONNumber.Create(Y)); + j.AddPair('text', cdo.Text[cdo.InsertType]); + BuilderCommand('charmapDragDrop', j); + finally + j.Free; + end; +end; + +procedure TframeTouchLayoutBuilder.SetupCharMapDrop; +begin + GetCharMapDropTool.Handle(cef.cefwp, cmimDefault, CharMapDragOver, CharMapDragDrop); +end; + end. diff --git a/windows/src/developer/TIKE/xml/layoutbuilder/builder.js b/windows/src/developer/TIKE/xml/layoutbuilder/builder.js index d6bb04d810..5d5ff7d953 100644 --- a/windows/src/developer/TIKE/xml/layoutbuilder/builder.js +++ b/windows/src/developer/TIKE/xml/layoutbuilder/builder.js @@ -1368,7 +1368,9 @@ $(function () { builder.generateSubKeys(); })); - builder.updateSubKeyCap = builder.wrapChange(function (val) { + builder.updateSubKeyCap = builder.wrapChange(function () { + var val = $(this).val(); + var k = builder.selectedSubKey(); if (k.length == 0) return; $('.text', k).text(builder.renameSpecialKey(val)); @@ -1794,6 +1796,55 @@ $(function () { builder.ctrlDown = false; }); + // + // Character map drag+drop and double-click insertion + // + + builder.charmapDragOver = function(o) { + + // Convert X, Y to document coordinates + + let target = document.elementFromPoint(o.x, o.y); + if(target === null || (target.nodeName != 'INPUT' && target.className != 'text')) { + return false; + } + + return true; + }; + + builder.charmapDragDrop = function(o) { + + // Convert X, Y to document coordinates + + if(o.x >= 0 && o.y >= 0) { + var target = document.elementFromPoint(o.x, o.y); + if(target === null) { + return false; + } + if(target.nodeName == 'INPUT') { + target = $(target); + } else if(target.className == 'text') { + if(target.parentElement.parentElement.id == 'sk') { + builder.selectSubKey(target.parentElement); + target = document.lastFocus; + } else { + builder.selectKey(target.parentElement); + target = document.lastFocus; + } + } else { + return false; + } + } else { + // Double-click insertion, so use last focused control + var target = $(document.lastFocus); + } + + // Focus the control and add the text + + target.focus(); + target.val(target.val() + o.text); + }; + builder.preparePlatforms(); builder.enableUndoControls(); });