Skip to content

Commit

Permalink
TextInput improvements.
Browse files Browse the repository at this point in the history
  • Loading branch information
dddomodossola committed Oct 25, 2018
1 parent 7fcce47 commit b64c7e6
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 104 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ The previous dialect is still compatible.

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

The event TextInput.onenter is no more supported.

The events TextInput.onkeydown and TextInput.onkeyup are now different, and require a different listener format. There is an additional parameter keycode.

The TextInput.onchange event now occurs also in case of Enter key pressed, if TextInput is single_line.


Getting Started
===
Expand Down
15 changes: 4 additions & 11 deletions editor/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,14 +566,7 @@ def main(self):
return false;""" % {'evt':self.EVENT_ONDROPPPED}
self.project.attributes['editor_varname'] = 'App'
self.project.attributes[self.project.EVENT_ONKEYDOWN] = """
var params={};
params['keypressed']=event.keyCode;
sendCallbackParam('%(id)s','%(evt)s',params);
if(event.keyCode==46){
return false;
}
""" % {'id':str(id(self)), 'evt':self.project.EVENT_ONKEYDOWN}
self.project.onkeydown.connect(self.onkeydown)

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

Expand Down Expand Up @@ -775,10 +768,10 @@ def toolbar_delete_clicked(self, widget):
self.selectedWidget = parent
print("tag deleted")

def onkeydown(self, keypressed):
if str(keypressed)=='46': #46 the delete keycode
def onkeydown(self, emitter, key, keycode, ctrl, shift, alt):
if str(keycode)=='46': #46 the delete keycode
self.toolbar_delete_clicked(None)
print("Key pressed: " + str(keypressed))
print("Key pressed: " + str(keycode))


def on_dropped(self, left, top):
Expand Down
32 changes: 3 additions & 29 deletions editor/editor_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def show(self, *args):

def add_fileinput_field(self, defaultname='untitled'):
self.txtFilename = gui.TextInput()
self.txtFilename.onenter.connect(self.on_enter_key_pressed)
self.txtFilename.onchange.connect(self.on_enter_key_pressed)
self.txtFilename.set_text(defaultname)

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

self.txtInput = StringEditor(width='80%', height='100%')
self.txtInput = gui.TextInput(width='80%', height='100%')
self.txtInput.style['float'] = 'left'
self.txtInput.onchange.connect(self.on_txt_changed)
self.append(self.txtInput)
Expand Down Expand Up @@ -696,32 +696,6 @@ def set_value(self, value):
self.txtInput.set_value(value)


class StringEditor(gui.TextInput, gui.EventSource):
""" This class sends the input directly to the listener, but don't applies the changes
to the widget itself in order to avoid to get updated losting the focus.
The value will be committed to the widget itself when blurs.
"""
def __init__(self, *args, **kwargs):
super(StringEditor, self).__init__(True, *args, **kwargs)
gui.EventSource.__init__(self)
self.attributes[self.EVENT_ONBLUR] = \
"""var elem=document.getElementById('%(id)s');elem.value = elem.value.split('\\n').join('');
var params={};params['new_value']=elem.value;
sendCallbackParam('%(id)s','%(evt)s',params);""" % {'id': self.identifier, 'evt': self.EVENT_ONCHANGE}

self.attributes[self.EVENT_ONKEYUP] = \
"""var elem=document.getElementById('%(id)s');elem.value = elem.value.split('\\n').join('');
var params={};params['new_value']=elem.value;
sendCallbackParam('%(id)s','%(evt)s',params);""" % {'id': self.identifier, 'evt': self.EVENT_ONKEYUP}

self.attributes[self.EVENT_ONKEYDOWN] = \
"""if((event.charCode||event.keyCode)==13){event.keyCode = 0;event.charCode = 0; return false;}""" % {'id': self.identifier}

@gui.decorate_event
def onkeyup(self, new_value):
return (new_value,)


#widget that allows to edit a specific html and css attributes
# it has a descriptive label, an edit widget (TextInput, SpinBox..) based on the 'type' and a title
class EditorAttributeInput(gui.Widget, gui.EventSource):
Expand Down Expand Up @@ -768,7 +742,7 @@ def __init__(self, attributeName, attributeDict, appInstance=None):
self.inputWidget = CssSizeInput(appInstance)

else: #default editor is string
self.inputWidget = StringEditor()
self.inputWidget = gui.TextInput()

self.inputWidget.onchange.connect(self.on_attribute_changed)
self.inputWidget.set_size('50%','22px')
Expand Down
115 changes: 52 additions & 63 deletions remi/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ class Widget(Tag, EventSource):
EVENT_ONKEYPRESS = 'onkeypress'
EVENT_ONKEYUP = 'onkeyup'
EVENT_ONCHANGE = 'onchange'
EVENT_ONINPUT = 'oninput'
EVENT_ONFOCUS = 'onfocus'
EVENT_ONBLUR = 'onblur'
EVENT_ONCONTEXTMENU = "oncontextmenu"
Expand Down Expand Up @@ -819,39 +820,43 @@ def ontouchcancel(self):
"""
return ()

@decorate_set_on_listener("(self, emitter, key, ctrl, shift, alt)")
@decorate_set_on_listener("(self, emitter, key, keycode, ctrl, shift, alt)")
@decorate_event_js("""var params={};params['key']=event.key;
params['keycode']=(event.which||event.keyCode);
params['ctrl']=event.ctrlKey;
params['shift']=event.shiftKey;
params['alt']=event.altKey;
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
event.stopPropagation();event.preventDefault();return false;""")
def onkeyup(self, key, ctrl, shift, alt):
def onkeyup(self, key, keycode, ctrl, shift, alt):
"""Called when user types and releases a key.
The widget should be able to receive the focus in order to emit the event.
Assign a 'tabindex' attribute to make it focusable.
Args:
key (str): the character value
keycode (str): the numeric char code
"""
return (key, ctrl, shift, alt)
return (key, keycode, ctrl, shift, alt)

@decorate_set_on_listener("(self, emitter, key, ctrl, shift, alt)")
@decorate_set_on_listener("(self, emitter, key, keycode, ctrl, shift, alt)")
@decorate_event_js("""var params={};params['key']=event.key;
params['keycode']=(event.which||event.keyCode);
params['ctrl']=event.ctrlKey;
params['shift']=event.shiftKey;
params['alt']=event.altKey;
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
event.stopPropagation();event.preventDefault();return false;""")
def onkeydown(self, key, ctrl, shift, alt):
def onkeydown(self, key, keycode, ctrl, shift, alt):
"""Called when user types and releases a key.
The widget should be able to receive the focus in order to emit the event.
Assign a 'tabindex' attribute to make it focusable.
Args:
key (str): the character value
keycode (str): the numeric char code
"""
return (key, ctrl, shift, alt)
return (key, keycode, ctrl, shift, alt)

@decorate_explicit_alias_for_listener_registration
def set_on_focus_listener(self, callback, *userdata):
Expand Down Expand Up @@ -1267,9 +1272,20 @@ def __init__(self, single_line=True, hint='', *args, **kwargs):
if single_line:
self.style['resize'] = 'none'
self.attributes['rows'] = '1'
self.attributes[self.EVENT_ONKEYDOWN] = "if((event.charCode||event.keyCode)==13){" \
"event.keyCode = 0;event.charCode = 0; document.getElementById('%(id)s').blur();" \
"return false;}" % {'id': self.identifier}
self.attributes[self.EVENT_ONINPUT] = """
var elem = document.getElementById('%(emitter_identifier)s');
var enter_pressed = (elem.value.indexOf('\\n') > -1);
if(enter_pressed){
elem.value = elem.value.split('\\n').join('');
var params={};params['new_value']=elem.value;
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
}""" % {'emitter_identifier': str(self.identifier), 'event_name': Widget.EVENT_ONCHANGE}
#else:
# self.attributes[self.EVENT_ONINPUT] = """
# var elem = document.getElementById('%(emitter_identifier)s');
# var params={};params['new_value']=elem.value;
# sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
# """ % {'emitter_identifier': str(self.identifier), 'event_name': Widget.EVENT_ONCHANGE}

self.set_value('')

Expand Down Expand Up @@ -1303,70 +1319,47 @@ def get_value(self):
@decorate_set_on_listener("(self, emitter, new_value)")
@decorate_event
def onchange(self, new_value):
"""Called when the user finishes to edit the TextInput content.
"""Called when the user changes the TextInput content.
With single_line=True it fires in case of focus lost and Enter key pressed.
With single_line=False it fires at each key released.
Args:
new_value (str): the new string content of the TextInput.
"""
self.disable_refresh()
self.set_value(new_value)
self.enable_refresh()
return (new_value, )

@decorate_set_on_listener("(self, emitter, new_value)")
@decorate_event_js("""var elem=document.getElementById('%(emitter_identifier)s');elem.value = elem.value.split('\\n').join('');
var params={};params['new_value']=elem.value;
@decorate_set_on_listener("(self, emitter, new_value, keycode)")
@decorate_event_js("""var elem=document.getElementById('%(emitter_identifier)s');
var params={};params['new_value']=elem.value;params['keycode']=(event.which||event.keyCode);
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);""")
def onkeyup(self, new_value):
def onkeyup(self, new_value, keycode):
"""Called when user types and releases a key into the TextInput
Note: This event can't be registered together with Widget.onchange.
Args:
new_value (str): the new string content of the TextInput
keycode (str): the numeric char code
"""
self.disable_refresh()
self.set_value(new_value)
self.enable_refresh()
self._set_updated()
return (new_value, )
return (new_value, keycode)

@decorate_set_on_listener("(self, emitter, new_value)")
@decorate_event_js("var params={};params['new_value']=document.getElementById('%(emitter_identifier)s').value;" \
"sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);if((event.charCode||event.keyCode)==13){" \
"event.keyCode = 0;event.charCode = 0; document.getElementById('%(emitter_identifier)s').blur(); return false;}")
def onkeydown(self, new_value):
@decorate_set_on_listener("(self, emitter, new_value, keycode)")
@decorate_event_js("""var elem=document.getElementById('%(emitter_identifier)s');
var params={};params['new_value']=elem.value;params['keycode']=(event.which||event.keyCode);
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);""")
def onkeydown(self, new_value, keycode):
"""Called when the user types a key into the TextInput.
Note: This event can't be registered together with Widget.onenter.
Args:
new_value (str): the new string content of the TextInput.
"""
self.disable_refresh()
self.set_value(new_value)
self.enable_refresh()
self._set_updated()
return (new_value, )

@decorate_set_on_listener("(self, emitter, new_value)")
@decorate_event_js("""
if (event.keyCode == 13) {
var params={};
params['new_value']=document.getElementById('%(emitter_identifier)s').value;
document.getElementById('%(emitter_identifier)s').value = '';
document.getElementById('%(emitter_identifier)s').onchange = '';
sendCallbackParam('%(emitter_identifier)s','%(event_name)s',params);
return false;
}""")
def onenter(self, new_value):
"""Called when the user types an ENTER into the TextInput.
Note: This event can't be registered together with Widget.onkeydown.
Note: This event can't be registered together with Widget.onchange.
Args:
new_value (str): the new string content of the TextInput.
keycode (str): the numeric char code
"""
self.disable_refresh()
self.set_value(new_value)
self.enable_refresh()
self._set_updated()
return (new_value, )
return (new_value, keycode)

@decorate_explicit_alias_for_listener_registration
def set_on_change_listener(self, callback, *userdata):
Expand All @@ -1380,10 +1373,6 @@ def set_on_key_up_listener(self, callback, *userdata):
def set_on_key_down_listener(self, callback, *userdata):
self.onkeydown.connect(callback, *userdata)

@decorate_explicit_alias_for_listener_registration
def set_on_enter_listener(self, callback, *userdata):
self.onenter.connect(callback, *userdata)


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

self.inputText = TextInput()
self.inputText.onenter.connect(self.on_text_enter_listener)
self.inputText.onkeydown.connect(self.on_keydown_listener)
self.add_field('textinput', self.inputText)
self.inputText.set_text(initial_value)

self.confirm_dialog.connect(self.confirm_value)

@decorate_set_on_listener("(self, emitter, value)")
@decorate_event
def on_text_enter_listener(self, widget, value):
def on_keydown_listener(self, widget, value, keycode):
"""event called pressing on ENTER key.
propagates the string content of the input field
"""
self.hide()
return (value, )
if keycode=="13":
self.hide()
self.inputText.set_text(value)
self.confirm_value(self)

@decorate_set_on_listener("(self, emitter, value)")
@decorate_event
Expand Down
12 changes: 11 additions & 1 deletion remi/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,15 @@ def _instance(self):
document.body.innerHTML += decodeURIComponent(content);
}else if( received_msg[0]=='1' ){ /*update_widget*/
var focusedElement=-1;
var caretStart=-1;
var caretEnd=-1;
if (document.activeElement)
{
focusedElement = document.activeElement.id;
try{
caretStart = document.activeElement.selectionStart;
caretEnd = document.activeElement.selectionEnd;
}catch(e){}
}
var index = received_msg.indexOf(',')+1;
var idElem = received_msg.substr(1,index-2);
Expand All @@ -436,7 +442,11 @@ def _instance(self):
var elemToFocus = document.getElementById(focusedElement);
if( elemToFocus != null ){
document.getElementById(focusedElement).focus();
elemToFocus.focus();
try{
elemToFocus = document.getElementById(focusedElement);
if(caretStart>-1 && caretEnd>-1) elemToFocus.setSelectionRange(caretStart, caretEnd);
}catch(e){}
}
}else if( received_msg[0]=='2' ){ /*javascript*/
var content = received_msg.substr(1,received_msg.length-1);
Expand Down

0 comments on commit b64c7e6

Please sign in to comment.