mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-10 10:55:33 +00:00
feat(developer): improve Touch Layout Editor and Character Map integration
Fixes #4854. Adds some intelligence to the drag + drop integration with the Touch Layout Editor, so that we always drop a character onto a key cap, we always drop a code onto a ID field, and can drop both character + code together (if the key id is T_new_..., or holding Ctrl key). Hovering over any key for 0.25 sec while dragging will select that key. You can then continue dragging down to the subkey array and drop onto any of those keys. Note that you do not need to hover for 0.25 sec before you drop onto a key -- the drop operation will select the key under the cursor when dropping. Double-clicking will update the selected key or subkey; Ctrl will do both the key cap and the ID.
This commit is contained in:
parent
83c8b87f42
commit
33fda3e59f
4 changed files with 206 additions and 59 deletions
|
|
@ -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;
|
||||
|
|
|
|||
188
windows/src/developer/TIKE/xml/layoutbuilder/builder-charmap.js
Normal file
188
windows/src/developer/TIKE/xml/layoutbuilder/builder-charmap.js
Normal file
|
|
@ -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);
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
<script><xsl:attribute name="src"><xsl:value-of select="/TouchLayoutBuilder/LibPath"/>jquery-ui.js</xsl:attribute></script>
|
||||
<script>var KVKL = <xsl:value-of select='/TouchLayoutBuilder/LayoutJS' />;</script>
|
||||
<script><xsl:attribute name="src"><xsl:value-of select="/TouchLayoutBuilder/LibPath"/>builder.js</xsl:attribute></script>
|
||||
<script><xsl:attribute name="src"><xsl:value-of select="/TouchLayoutBuilder/LibPath"/>builder-charmap.js</xsl:attribute></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wedgeAddRowAbove" class="kcontrol wedge-horz"><span>+</span></div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue