diff --git a/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas b/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas index c4e37c9100..78cb768d3a 100644 --- a/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas +++ b/windows/src/developer/TIKE/oskbuilder/UframeTouchLayoutBuilder.pas @@ -691,6 +691,12 @@ begin j.AddPair('x', TJSONNumber.Create(X)); j.AddPair('y', TJSONNumber.Create(Y)); j.AddPair('text', cdo.Text[cmimCharacter]); // It never makes sense to drop anything other than char + if GetKeyState(VK_SHIFT) < 0 then + j.AddPair('shift', TJSONBool.Create(True)); + if GetKeyState(VK_CONTROL) < 0 then + j.AddPair('ctrl', TJSONBool.Create(True)); + if GetKeyState(VK_MENU) < 0 then + j.AddPair('alt', TJSONBool.Create(True)); BuilderCommand('charmapDragDrop', j); finally j.Free; diff --git a/windows/src/developer/TIKE/xml/layoutbuilder/builder-charmap.js b/windows/src/developer/TIKE/xml/layoutbuilder/builder-charmap.js new file mode 100644 index 0000000000..3fadde85ea --- /dev/null +++ b/windows/src/developer/TIKE/xml/layoutbuilder/builder-charmap.js @@ -0,0 +1,188 @@ +// +// Character map drag+drop and double-click insertion +// + +builder.dragDrop = {}; +(function(builder, dragDrop) { + dragDrop.isSubKey = function(key) { + return key.parent().attr('id') == 'sk'; + }; + + dragDrop.getKeyElementFromElement = function(elem) { + if(elem.classList.contains('key')) { + return elem; + } + + if(!elem.parentElement) { + return null; + } + + if(elem.parentElement.classList.contains('key')) { + return elem.parentElement; + } + + return null; + }; + + dragDrop.getKeyElementFromDragTarget = function(target) { + let key = dragDrop.getKeyElementFromElement(target); + let isIdField = false; + + if(key) { + key = $(key); + } else { + if(target.id == 'inpKeyCap') { + key = builder.selectedKey(); + } else if(target.id == 'inpKeyName') { + key = builder.selectedKey(); + isIdField = true; + } else if(target.id == 'inpSubKeyCap') { + key = builder.selectedSubKey(); + } else if(target.id == 'inpSubKeyName') { + key = builder.selectedSubKey(); + isIdField = true; + } + + if(!key) { + return null; + } + } + + return { key: key, isIdField: isIdField }; + }; + + dragDrop.isHighSurrogate = function(character) { + let codeUnit = character.charCodeAt(0); + return codeUnit >= 0xD800 && codeUnit <= 0xDBFF; + }; + + dragDrop.isLowSurrogate = function(character) { + let codeUnit = character.charCodeAt(0); + return codeUnit >= 0xDC00 && codeUnit <= 0xDFFF; + }; + + dragDrop.surrogatePairToValue = function(character) { + let h = character.charCodeAt(0), l = character.charCodeAt(1); + return 0x10000 + (h - 0xD800) * 0x400 + (l - 0xDC00); + }; + + dragDrop.charToUnicodeValue = function(s) { + let v; + if(s.length == 2 && dragDrop.isHighSurrogate(s) && dragDrop.isLowSurrogate(s.substring(1))) { + v = builder.surrogatePairToValue(s); + } else if(s.length == 1) { + v = s.charCodeAt(0); + } else { + return null; + } + + let ch = v.toString(16); + while(ch.length < 4) ch = '0' + ch; + return ch; + }; + + dragDrop.keySelectTimeout = 0; + + dragDrop.clearKeySelectTimeout = function() { + if(dragDrop.keySelectTimeout) { + window.clearTimeout(dragDrop.keySelectTimeout); + dragDrop.keySelectTimeout = 0; + } + }; + + builder.charmapDragOver = function(o) { + // Convert X, Y to document coordinates + dragDrop.clearKeySelectTimeout(); + + const target = document.elementFromPoint(o.x, o.y); + const key = target ? dragDrop.getKeyElementFromDragTarget(target) : null; + const found = key && key.key && key.key.length; + + dragDrop.keySelectTimeout = window.setTimeout(function() { + // We use a timeout to allow dragging over to a key, then + // pausing to select it, and then dragging to a subkey of + // that key. + if(!found) { + builder.selectKey(null); + } else if(dragDrop.isSubKey(key.key)) { + builder.selectSubKey(key.key); + } else { + builder.selectKey(key.key); + } + dragDrop.keySelectTimeout = 0; + }, + // 1.0 sec if moving onto a blank space feels a little more + // friendly. We can pause a bit longer near the subkey array + // and not have it disappear + found ? 250 : 1000); + + return found; + }; + + builder.charmapDragDrop = function(o) { + + let key = null; + + dragDrop.clearKeySelectTimeout(); + + if(o.ctrl) { + // Ctrl as part of a drag/drop interaction + // so cancel the default select-key dialog + builder.ctrlDown = false; + // console.log('charmapDragDrop ctrlDown=false'); + } + + if(o.shift && o.ctrl) { + // o.shift means append to current key cap + // o.ctrl means set id value + // together they have no good meaning + return false; + } + + // Convert X, Y to document coordinates + + if(o.x >= 0 && o.y >= 0) { + let target = document.elementFromPoint(o.x, o.y); + if(target != null) { + key = dragDrop.getKeyElementFromDragTarget(target); + + if(key && key.key && key.key.length) { + if(dragDrop.isSubKey(key.key)) { + builder.selectSubKey(key.key); + } else { + builder.selectKey(key.key); + } + } + } + } else { + // Double-click insertion, so use last focused control + key = { key: builder.selectedSubKey(), isIdField: false }; + if(!key.key || !key.key.length) { + key = { key: builder.selectedKey(), isIdField: false }; + } + } + + if(!key || !key.key || !key.key.length) { + return false; + } + + // Focus the control and add the text + if(key.isIdField || key.key.data('id').startsWith('T_new_') || o.ctrl) { + let target = $(dragDrop.isSubKey(key.key) ? '#inpSubKeyName' : '#inpKeyName'); + target.val('U_'+dragDrop.charToUnicodeValue(o.text).toUpperCase()); + + if(key.isIdField) { + target.focus(); + } + + target.change(); + } + + if(!key.isIdField) { + let target = $(dragDrop.isSubKey(key.key) ? '#inpSubKeyCap' : '#inpKeyCap'); + target.focus(); + target.val((o.shift ? target.val() : '') + o.text); + target.change(); + } + }; +})(builder, builder.dragDrop); diff --git a/windows/src/developer/TIKE/xml/layoutbuilder/builder.js b/windows/src/developer/TIKE/xml/layoutbuilder/builder.js index 6d49f16c27..492eb37685 100644 --- a/windows/src/developer/TIKE/xml/layoutbuilder/builder.js +++ b/windows/src/developer/TIKE/xml/layoutbuilder/builder.js @@ -1,6 +1,6 @@ -$(function () { - window.builder = this; +window.builder = {}; +$(function() { (function(search) { var q = search.match(/Filename=(.+)(&|$)/); if(q) { @@ -1613,8 +1613,6 @@ $(function () { $('.text', nkey).text(this.renameSpecialKey(text)); builder.updateKeyId(nkey); } - //var div = document.createElement('div'); $(div).css('clear', 'both'); $('#sk').append(div); - builder.selectSubKey($('#sk > div')[0]); builder.prepareSubKey(); builder.enableSubKeyControls(); } @@ -1639,15 +1637,15 @@ $(function () { this.enableSubKeyControls = function () { var key = builder.selectedSubKey(); if (key.length == 0) { - $('#subKeyToolbar').css('visibility', 'hidden'); // attr('disabled', 'disabled'); + $('#subKeyToolbar').css('visibility', 'hidden'); } else { - $('#subKeyToolbar').css('visibility', ''); // removeAttr('disabled'); + $('#subKeyToolbar').css('visibility', ''); } } this.rescale = function () { builder.saveUndo(); - var keyId = builder.selectedKey().data('id'); //$('#kbd .key') + var keyId = builder.selectedKey().data('id'); builder.prepareLayer(); if (keyId !== null) builder.selectKey($('#kbd .key').filter(function (index) { return $(this).data('id') === keyId; }).first()); @@ -2172,7 +2170,11 @@ $(function () { } $(document).keydown(function (event) { - builder.ctrlDown = (event.which == 17); + if(!event.originalEvent.repeat) { + // We will get this event repeatedly while ctrl is held down, + // so if ctrlDown is cancelled, we don't want to set it again + builder.ctrlDown = (event.which == 17); + } if (builder.nextKeySelects) { $('#selectKeyDialog').dialog('close'); event.preventDefault(); @@ -2192,56 +2194,6 @@ $(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); - target.change(); - }; - builder.loadingState = true; builder.saveState = function() { @@ -2321,4 +2273,4 @@ $(function () { builder.enableUndoControls(); builder.loadState(); -}); +}.bind(builder)); diff --git a/windows/src/developer/TIKE/xml/layoutbuilder/builder.xsl b/windows/src/developer/TIKE/xml/layoutbuilder/builder.xsl index 6c0221a3c8..31312068ba 100644 --- a/windows/src/developer/TIKE/xml/layoutbuilder/builder.xsl +++ b/windows/src/developer/TIKE/xml/layoutbuilder/builder.xsl @@ -18,6 +18,7 @@ +