Skip to content

Commit b64c7e6

Browse files
committed
TextInput improvements.
1 parent 7fcce47 commit b64c7e6

5 files changed

Lines changed: 76 additions & 104 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ The previous dialect is still compatible.
4949

5050
The parameter host_name is now deprecated. The server automatically catches the address where to connect from the HTTP request.
5151

52+
The event TextInput.onenter is no more supported.
53+
54+
The events TextInput.onkeydown and TextInput.onkeyup are now different, and require a different listener format. There is an additional parameter keycode.
55+
56+
The TextInput.onchange event now occurs also in case of Enter key pressed, if TextInput is single_line.
57+
5258

5359
Getting Started
5460
===

editor/editor.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -566,14 +566,7 @@ def main(self):
566566
567567
return false;""" % {'evt':self.EVENT_ONDROPPPED}
568568
self.project.attributes['editor_varname'] = 'App'
569-
self.project.attributes[self.project.EVENT_ONKEYDOWN] = """
570-
var params={};
571-
params['keypressed']=event.keyCode;
572-
sendCallbackParam('%(id)s','%(evt)s',params);
573-
if(event.keyCode==46){
574-
return false;
575-
}
576-
""" % {'id':str(id(self)), 'evt':self.project.EVENT_ONKEYDOWN}
569+
self.project.onkeydown.connect(self.onkeydown)
577570

578571
self.projectConfiguration = editor_widgets.ProjectConfigurationDialog('Project Configuration', 'Write here the configuration for your project.')
579572

@@ -775,10 +768,10 @@ def toolbar_delete_clicked(self, widget):
775768
self.selectedWidget = parent
776769
print("tag deleted")
777770

778-
def onkeydown(self, keypressed):
779-
if str(keypressed)=='46': #46 the delete keycode
771+
def onkeydown(self, emitter, key, keycode, ctrl, shift, alt):
772+
if str(keycode)=='46': #46 the delete keycode
780773
self.toolbar_delete_clicked(None)
781-
print("Key pressed: " + str(keypressed))
774+
print("Key pressed: " + str(keycode))
782775

783776

784777
def on_dropped(self, left, top):

editor/editor_widgets.py

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ def show(self, *args):
306306

307307
def add_fileinput_field(self, defaultname='untitled'):
308308
self.txtFilename = gui.TextInput()
309-
self.txtFilename.onenter.connect(self.on_enter_key_pressed)
309+
self.txtFilename.onchange.connect(self.on_enter_key_pressed)
310310
self.txtFilename.set_text(defaultname)
311311

312312
self.add_field_with_label("filename","Filename",self.txtFilename)
@@ -662,7 +662,7 @@ def __init__(self, appInstance, **kwargs):
662662
self.style['display'] = 'block'
663663
self.style['overflow'] = 'hidden'
664664

665-
self.txtInput = StringEditor(width='80%', height='100%')
665+
self.txtInput = gui.TextInput(width='80%', height='100%')
666666
self.txtInput.style['float'] = 'left'
667667
self.txtInput.onchange.connect(self.on_txt_changed)
668668
self.append(self.txtInput)
@@ -696,32 +696,6 @@ def set_value(self, value):
696696
self.txtInput.set_value(value)
697697

698698

699-
class StringEditor(gui.TextInput, gui.EventSource):
700-
""" This class sends the input directly to the listener, but don't applies the changes
701-
to the widget itself in order to avoid to get updated losting the focus.
702-
The value will be committed to the widget itself when blurs.
703-
"""
704-
def __init__(self, *args, **kwargs):
705-
super(StringEditor, self).__init__(True, *args, **kwargs)
706-
gui.EventSource.__init__(self)
707-
self.attributes[self.EVENT_ONBLUR] = \
708-
"""var elem=document.getElementById('%(id)s');elem.value = elem.value.split('\\n').join('');
709-
var params={};params['new_value']=elem.value;
710-
sendCallbackParam('%(id)s','%(evt)s',params);""" % {'id': self.identifier, 'evt': self.EVENT_ONCHANGE}
711-
712-
self.attributes[self.EVENT_ONKEYUP] = \
713-
"""var elem=document.getElementById('%(id)s');elem.value = elem.value.split('\\n').join('');
714-
var params={};params['new_value']=elem.value;
715-
sendCallbackParam('%(id)s','%(evt)s',params);""" % {'id': self.identifier, 'evt': self.EVENT_ONKEYUP}
716-
717-
self.attributes[self.EVENT_ONKEYDOWN] = \
718-
"""if((event.charCode||event.keyCode)==13){event.keyCode = 0;event.charCode = 0; return false;}""" % {'id': self.identifier}
719-
720-
@gui.decorate_event
721-
def onkeyup(self, new_value):
722-
return (new_value,)
723-
724-
725699
#widget that allows to edit a specific html and css attributes
726700
# it has a descriptive label, an edit widget (TextInput, SpinBox..) based on the 'type' and a title
727701
class EditorAttributeInput(gui.Widget, gui.EventSource):
@@ -768,7 +742,7 @@ def __init__(self, attributeName, attributeDict, appInstance=None):
768742
self.inputWidget = CssSizeInput(appInstance)
769743

770744
else: #default editor is string
771-
self.inputWidget = StringEditor()
745+
self.inputWidget = gui.TextInput()
772746

773747
self.inputWidget.onchange.connect(self.on_attribute_changed)
774748
self.inputWidget.set_size('50%','22px')

remi/gui.py

Lines changed: 52 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ class Widget(Tag, EventSource):
473473
EVENT_ONKEYPRESS = 'onkeypress'
474474
EVENT_ONKEYUP = 'onkeyup'
475475
EVENT_ONCHANGE = 'onchange'
476+
EVENT_ONINPUT = 'oninput'
476477
EVENT_ONFOCUS = 'onfocus'
477478
EVENT_ONBLUR = 'onblur'
478479
EVENT_ONCONTEXTMENU = "oncontextmenu"
@@ -819,39 +820,43 @@ def ontouchcancel(self):
819820
"""
820821
return ()
821822

822-
@decorate_set_on_listener("(self, emitter, key, ctrl, shift, alt)")
823+
@decorate_set_on_listener("(self, emitter, key, keycode, ctrl, shift, alt)")
823824
@decorate_event_js("""var params={};params['key']=event.key;
825+
params['keycode']=(event.which||event.keyCode);
824826
params['ctrl']=event.ctrlKey;
825827
params['shift']=event.shiftKey;
826828
params['alt']=event.altKey;
827829
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
828830
event.stopPropagation();event.preventDefault();return false;""")
829-
def onkeyup(self, key, ctrl, shift, alt):
831+
def onkeyup(self, key, keycode, ctrl, shift, alt):
830832
"""Called when user types and releases a key.
831833
The widget should be able to receive the focus in order to emit the event.
832834
Assign a 'tabindex' attribute to make it focusable.
833835
834836
Args:
835837
key (str): the character value
838+
keycode (str): the numeric char code
836839
"""
837-
return (key, ctrl, shift, alt)
840+
return (key, keycode, ctrl, shift, alt)
838841

839-
@decorate_set_on_listener("(self, emitter, key, ctrl, shift, alt)")
842+
@decorate_set_on_listener("(self, emitter, key, keycode, ctrl, shift, alt)")
840843
@decorate_event_js("""var params={};params['key']=event.key;
844+
params['keycode']=(event.which||event.keyCode);
841845
params['ctrl']=event.ctrlKey;
842846
params['shift']=event.shiftKey;
843847
params['alt']=event.altKey;
844848
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
845849
event.stopPropagation();event.preventDefault();return false;""")
846-
def onkeydown(self, key, ctrl, shift, alt):
850+
def onkeydown(self, key, keycode, ctrl, shift, alt):
847851
"""Called when user types and releases a key.
848852
The widget should be able to receive the focus in order to emit the event.
849853
Assign a 'tabindex' attribute to make it focusable.
850854
851855
Args:
852856
key (str): the character value
857+
keycode (str): the numeric char code
853858
"""
854-
return (key, ctrl, shift, alt)
859+
return (key, keycode, ctrl, shift, alt)
855860

856861
@decorate_explicit_alias_for_listener_registration
857862
def set_on_focus_listener(self, callback, *userdata):
@@ -1267,9 +1272,20 @@ def __init__(self, single_line=True, hint='', *args, **kwargs):
12671272
if single_line:
12681273
self.style['resize'] = 'none'
12691274
self.attributes['rows'] = '1'
1270-
self.attributes[self.EVENT_ONKEYDOWN] = "if((event.charCode||event.keyCode)==13){" \
1271-
"event.keyCode = 0;event.charCode = 0; document.getElementById('%(id)s').blur();" \
1272-
"return false;}" % {'id': self.identifier}
1275+
self.attributes[self.EVENT_ONINPUT] = """
1276+
var elem = document.getElementById('%(emitter_identifier)s');
1277+
var enter_pressed = (elem.value.indexOf('\\n') > -1);
1278+
if(enter_pressed){
1279+
elem.value = elem.value.split('\\n').join('');
1280+
var params={};params['new_value']=elem.value;
1281+
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
1282+
}""" % {'emitter_identifier': str(self.identifier), 'event_name': Widget.EVENT_ONCHANGE}
1283+
#else:
1284+
# self.attributes[self.EVENT_ONINPUT] = """
1285+
# var elem = document.getElementById('%(emitter_identifier)s');
1286+
# var params={};params['new_value']=elem.value;
1287+
# sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
1288+
# """ % {'emitter_identifier': str(self.identifier), 'event_name': Widget.EVENT_ONCHANGE}
12731289

12741290
self.set_value('')
12751291

@@ -1303,70 +1319,47 @@ def get_value(self):
13031319
@decorate_set_on_listener("(self, emitter, new_value)")
13041320
@decorate_event
13051321
def onchange(self, new_value):
1306-
"""Called when the user finishes to edit the TextInput content.
1322+
"""Called when the user changes the TextInput content.
1323+
With single_line=True it fires in case of focus lost and Enter key pressed.
1324+
With single_line=False it fires at each key released.
13071325
13081326
Args:
13091327
new_value (str): the new string content of the TextInput.
13101328
"""
1329+
self.disable_refresh()
13111330
self.set_value(new_value)
1331+
self.enable_refresh()
13121332
return (new_value, )
13131333

1314-
@decorate_set_on_listener("(self, emitter, new_value)")
1315-
@decorate_event_js("""var elem=document.getElementById('%(emitter_identifier)s');elem.value = elem.value.split('\\n').join('');
1316-
var params={};params['new_value']=elem.value;
1334+
@decorate_set_on_listener("(self, emitter, new_value, keycode)")
1335+
@decorate_event_js("""var elem=document.getElementById('%(emitter_identifier)s');
1336+
var params={};params['new_value']=elem.value;params['keycode']=(event.which||event.keyCode);
13171337
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);""")
1318-
def onkeyup(self, new_value):
1338+
def onkeyup(self, new_value, keycode):
13191339
"""Called when user types and releases a key into the TextInput
13201340
1341+
Note: This event can't be registered together with Widget.onchange.
1342+
13211343
Args:
13221344
new_value (str): the new string content of the TextInput
1345+
keycode (str): the numeric char code
13231346
"""
1324-
self.disable_refresh()
1325-
self.set_value(new_value)
1326-
self.enable_refresh()
1327-
self._set_updated()
1328-
return (new_value, )
1347+
return (new_value, keycode)
13291348

1330-
@decorate_set_on_listener("(self, emitter, new_value)")
1331-
@decorate_event_js("var params={};params['new_value']=document.getElementById('%(emitter_identifier)s').value;" \
1332-
"sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);if((event.charCode||event.keyCode)==13){" \
1333-
"event.keyCode = 0;event.charCode = 0; document.getElementById('%(emitter_identifier)s').blur(); return false;}")
1334-
def onkeydown(self, new_value):
1349+
@decorate_set_on_listener("(self, emitter, new_value, keycode)")
1350+
@decorate_event_js("""var elem=document.getElementById('%(emitter_identifier)s');
1351+
var params={};params['new_value']=elem.value;params['keycode']=(event.which||event.keyCode);
1352+
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);""")
1353+
def onkeydown(self, new_value, keycode):
13351354
"""Called when the user types a key into the TextInput.
13361355
1337-
Note: This event can't be registered together with Widget.onenter.
1338-
1339-
Args:
1340-
new_value (str): the new string content of the TextInput.
1341-
"""
1342-
self.disable_refresh()
1343-
self.set_value(new_value)
1344-
self.enable_refresh()
1345-
self._set_updated()
1346-
return (new_value, )
1347-
1348-
@decorate_set_on_listener("(self, emitter, new_value)")
1349-
@decorate_event_js("""
1350-
if (event.keyCode == 13) {
1351-
var params={};
1352-
params['new_value']=document.getElementById('%(emitter_identifier)s').value;
1353-
document.getElementById('%(emitter_identifier)s').value = '';
1354-
document.getElementById('%(emitter_identifier)s').onchange = '';
1355-
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
1356-
return false;
1357-
}""")
1358-
def onenter(self, new_value):
1359-
"""Called when the user types an ENTER into the TextInput.
1360-
Note: This event can't be registered together with Widget.onkeydown.
1356+
Note: This event can't be registered together with Widget.onchange.
13611357
13621358
Args:
13631359
new_value (str): the new string content of the TextInput.
1360+
keycode (str): the numeric char code
13641361
"""
1365-
self.disable_refresh()
1366-
self.set_value(new_value)
1367-
self.enable_refresh()
1368-
self._set_updated()
1369-
return (new_value, )
1362+
return (new_value, keycode)
13701363

13711364
@decorate_explicit_alias_for_listener_registration
13721365
def set_on_change_listener(self, callback, *userdata):
@@ -1380,10 +1373,6 @@ def set_on_key_up_listener(self, callback, *userdata):
13801373
def set_on_key_down_listener(self, callback, *userdata):
13811374
self.onkeydown.connect(callback, *userdata)
13821375

1383-
@decorate_explicit_alias_for_listener_registration
1384-
def set_on_enter_listener(self, callback, *userdata):
1385-
self.onenter.connect(callback, *userdata)
1386-
13871376

13881377
class Label(Widget, _MixinTextualWidget):
13891378
""" Non editable text label widget. Set its content by means of set_text function, and retrieve its content with the
@@ -1594,21 +1583,21 @@ def __init__(self, title='Title', message='Message', initial_value='', *args, **
15941583
super(InputDialog, self).__init__(title, message, *args, **kwargs)
15951584

15961585
self.inputText = TextInput()
1597-
self.inputText.onenter.connect(self.on_text_enter_listener)
1586+
self.inputText.onkeydown.connect(self.on_keydown_listener)
15981587
self.add_field('textinput', self.inputText)
15991588
self.inputText.set_text(initial_value)
16001589

16011590
self.confirm_dialog.connect(self.confirm_value)
16021591

1603-
@decorate_set_on_listener("(self, emitter, value)")
1604-
@decorate_event
1605-
def on_text_enter_listener(self, widget, value):
1592+
def on_keydown_listener(self, widget, value, keycode):
16061593
"""event called pressing on ENTER key.
16071594
16081595
propagates the string content of the input field
16091596
"""
1610-
self.hide()
1611-
return (value, )
1597+
if keycode=="13":
1598+
self.hide()
1599+
self.inputText.set_text(value)
1600+
self.confirm_value(self)
16121601

16131602
@decorate_set_on_listener("(self, emitter, value)")
16141603
@decorate_event

remi/server.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,15 @@ def _instance(self):
415415
document.body.innerHTML += decodeURIComponent(content);
416416
}else if( received_msg[0]=='1' ){ /*update_widget*/
417417
var focusedElement=-1;
418+
var caretStart=-1;
419+
var caretEnd=-1;
418420
if (document.activeElement)
419421
{
420422
focusedElement = document.activeElement.id;
423+
try{
424+
caretStart = document.activeElement.selectionStart;
425+
caretEnd = document.activeElement.selectionEnd;
426+
}catch(e){}
421427
}
422428
var index = received_msg.indexOf(',')+1;
423429
var idElem = received_msg.substr(1,index-2);
@@ -436,7 +442,11 @@ def _instance(self):
436442
437443
var elemToFocus = document.getElementById(focusedElement);
438444
if( elemToFocus != null ){
439-
document.getElementById(focusedElement).focus();
445+
elemToFocus.focus();
446+
try{
447+
elemToFocus = document.getElementById(focusedElement);
448+
if(caretStart>-1 && caretEnd>-1) elemToFocus.setSelectionRange(caretStart, caretEnd);
449+
}catch(e){}
440450
}
441451
}else if( received_msg[0]=='2' ){ /*javascript*/
442452
var content = received_msg.substr(1,received_msg.length-1);

0 commit comments

Comments
 (0)