class select(CompositePres): def __init__(self, option_value_content_pairs, value, on_choose=None): """ HTML select control :param option_value_content_pairs: a sequence of tuple-pairs describing the options. Each pair is of the form (value, text) :param value: the initial value :param on_choose: a callback function of the form fn(event, value) that is invoked when the user makes a choice """ self.__options = [ Html( '<option value="{0}"{1}>'.format( opt_value, (' selected' if opt_value == value else '')), content, '</option>') for opt_value, content in option_value_content_pairs ] self.choose = EventHandler() if on_choose is not None: self.choose.connect(on_choose) def pres(self, pres_ctx): p = Html( *(['<select>'] + self.__options + ['</select>'])).js_function_call('larch.controls.initSelect') p = p.with_event_handler('select_choose', lambda event: self.choose(event, event.data)) p = p.use_js('/static/larch/larch_ui.js').use_css( '/static/larch/larch_ui.css') return p
class spinner(CompositePres): def __init__(self, on_change=None, value=0, input_name='spinner'): """ jQuery UI spinner control :param on_change: callback that is invoked when the spinner's value changes; function(value) :param value: [optional] initial value :param input_name: [optional] name attribute for input tag """ self.change = EventHandler() self.__value = value self.__input_name = input_name self.__channel = MessageChannel() if on_change is not None: self.change.connect(on_change) def __on_spinner_change(self, event): self.change(event, event.data) def set_value(self, value): self.__channel.send(value) def pres(self, pres_ctx): spin = Html('<input name={0} value={1} />'.format( self.__input_name, self.__value)) spin = spin.js_function_call('larch.controls.initSpinner', self.__channel) spin = spin.with_event_handler("spinner_change", self.__on_spinner_change) spin = spin.use_js('/static/larch/larch_ui.js').use_css( '/static/larch/larch_ui.css') return spin
class focusable(CompositePres): def __init__(self, contents, on_gain_focus=None, on_lose_focus=None): """ Make an element focusable :param contents: contents that are to be made focusable :param on_gain_focus: [optional] a function of the form function(event) that is invoked when the element gains focus :param on_lose_focus: [optional] a function of the form function(event) that is invoked when the element loses focus :return: the control """ self.__contents = contents self.gain_focus = EventHandler() self.lose_focus = EventHandler() if on_gain_focus is not None: self.gain_focus.connect(on_gain_focus) if on_lose_focus is not None: self.lose_focus.connect(on_lose_focus) def pres(self, pres_ctx): p = Html(self.__contents).js_function_call( 'larch.controls.initFocusable').js_shutdown_function_call( 'larch.controls.shutdownFocusable') p = p.with_event_handler('gain_focus', self.gain_focus) p = p.with_event_handler('lose_focus', self.lose_focus) p = p.use_js('/static/larch/larch_ui.js').use_css( '/static/larch/larch_ui.css') return p
class button(CompositePres): def __init__(self, text=None, action_fn=None, primary_icon=None, secondary_icon=None, disabled=False): """ Create a JQuery UI button :param text: the button content (can include HTML) :param action_fn: a callback that is invoked when the button is pressed, of the form function(event) :param primary_icon: primary icon (see JQuery UI icon classes) :param secondary_icon: secondary icon (see JQuery UI icon classes) :param disabled: disable the button :return: the button control """ self.__text = text self.clicked = EventHandler() self.__primary_icon = primary_icon self.__secondary_icon = secondary_icon self.__disabled = disabled if action_fn is not None: self.clicked.connect(action_fn) def pres(self, pres_ctx): options = {} if self.__disabled is not False: options['disabled'] = True if self.__primary_icon is not None or self.__secondary_icon is not None: icons = {} if self.__primary_icon is not None: icons['primary'] = self.__primary_icon if self.__secondary_icon is not None: icons['secondary'] = self.__secondary_icon options['icons'] = icons if self.__text is not None: p = Html( '<button type="button" onclick="{0}">'.format( post_event_js_code_for_handler('clicked')), self.__text, '</button>').js_function_call('larch.controls.initButton', options) else: options['text'] = False p = Html('<button type="button" onclick="{0}"></button>' ).js_function_call('larch.controls.initButton', options) p = p.with_event_handler('clicked', self.clicked).use_js( '/static/larch/larch_ui.js').use_css('/static/larch/larch_ui.css') return p
class item(CompositePres): def __init__(self, item_content, on_select=None): """ Create a menu item control. Must be placed within a menu control, created with :menu:. :param item_content: the HTML content of the menu item :param on_select: a callback invoked when the menu item is activated by the user :return: the menu item control """ self.__item_content = item_content self.select = EventHandler() if on_select is not None: self.select.connect(on_select) def pres(self, pres_ctx): p = Html('<li><a>', self.__item_content, '</a></li>') p = p.with_event_handler('menu_select', self.select) return p
class select (CompositePres): def __init__(self, option_value_content_pairs, value, on_choose=None): """ HTML select control :param option_value_content_pairs: a sequence of tuple-pairs describing the options. Each pair is of the form (value, text) :param value: the initial value :param on_choose: a callback function of the form fn(event, value) that is invoked when the user makes a choice """ self.__options = [Html('<option value="{0}"{1}>'.format(opt_value, (' selected' if opt_value == value else '')), content, '</option>') for opt_value, content in option_value_content_pairs] self.choose = EventHandler() if on_choose is not None: self.choose.connect(on_choose) def pres(self, pres_ctx): p = Html(*(['<select>'] + self.__options + ['</select>'])).js_function_call('larch.controls.initSelect') p = p.with_event_handler('select_choose', lambda event: self.choose(event, event.data)) p = p.use_js('/files/static/larch/larch_ui.js').use_css('/files/static/larch/larch_ui.css') return p
class action_link (CompositePres): def __init__(self, link_text, action_fn=None, css_class=None): """ Create an action link that invokes a function in response to the user clicking it :param link_text: the link text :param action_fn: a callback that is called when the link is clicked, of the form function(event) :param css_class: an optional css class :return: the control """ self.__link_text = link_text self.clicked = EventHandler() if action_fn is not None: self.clicked.connect(action_fn) self.__css_class = css_class def pres(self, pres_ctx): css = ' class="{0}"'.format(self.__css_class) if self.__css_class is not None else '' p = Html('<a {2}href="javascript:" onclick="{0}">{1}</a>'.format(post_event_js_code_for_handler('clicked'), self.__link_text, css)) p = p.with_event_handler('clicked', self.clicked) return p
class button (CompositePres): def __init__(self, text=None, action_fn=None, primary_icon=None, secondary_icon=None, disabled=False): """ Create a JQuery UI button :param text: the button content (can include HTML) :param action_fn: a callback that is invoked when the button is pressed, of the form function(event) :param primary_icon: primary icon (see JQuery UI icon classes) :param secondary_icon: secondary icon (see JQuery UI icon classes) :param disabled: disable the button :return: the button control """ self.__text = text self.clicked = EventHandler() self.__primary_icon = primary_icon self.__secondary_icon = secondary_icon self.__disabled = disabled if action_fn is not None: self.clicked.connect(action_fn) def pres(self, pres_ctx): options = {} if self.__disabled is not False: options['disabled'] = True if self.__primary_icon is not None or self.__secondary_icon is not None: icons = {} if self.__primary_icon is not None: icons['primary'] = self.__primary_icon if self.__secondary_icon is not None: icons['secondary'] = self.__secondary_icon options['icons'] = icons if self.__text is not None: p = Html('<button type="button" onclick="{0}">'.format(post_event_js_code_for_handler('clicked')), self.__text, '</button>').js_function_call('larch.controls.initButton', options) else: options['text'] = False p = Html('<button type="button" onclick="{0}"></button>').js_function_call('larch.controls.initButton', options) p = p.with_event_handler('clicked', self.clicked).use_js('/files/static/larch/larch_ui.js').use_css('/files/static/larch/larch_ui.css') return p
class text_entry(CompositePres): def __init__(self, text, immediate_events=False, on_edit=None, width=None): """ Create a text entry control :param text: the initial text to display in the control :param immediate_events: if True, an event will be emitted each time the text is edited (on each keypress) :param on_edit: a callback invoked in response to edits, of the form function(modified_text) :param width: width of the control; ints or longs will be interpreted as width in pixels, otherwise use string in CSS form, e.g. '100px', '10em' or '50%' :return: the editor control """ self.edit = EventHandler() if isinstance(width, int) or isinstance(width, long): width = '{0}px'.format(width) self.__text = text self.__immediate_events = immediate_events self.__width = width if on_edit is not None: self.edit.connect(on_edit) self.__channel = MessageChannel() def set_text(self, text): self.__channel.send(text) def pres(self, pres_ctx): sz = '' if self.__width is not None: sz = ' style="width: {0};"'.format(self.__width) p = Html('<input type="text" value="{0}"{1}></input>'.format( self.__text, sz)).js_function_call('larch.controls.initTextEntry', self.__immediate_events, self.__channel) p = p.with_event_handler('text_entry_edit', lambda event: self.edit(event, event.data)) p = p.use_js('/static/larch/larch_ui.js').use_css( '/static/larch/larch_ui.css') return p
class form(CompositePres): def __init__(self, contents, on_submit=None): """ Wrap the contents in a form that will be sent to Larch :param contents: The contents of the form, including the enclosing <form> tag :param on_submit: A callback invoked when the form is submitted. Callback signature: function(event); the form data is accessible through event.data :return: the form control """ self.__contents = contents self.submit = EventHandler() if on_submit is not None: self.submit.connect(on_submit) def pres(self, pres_ctx): p = Html('<form enctype="multipart/form-data">', self.__contents, '</form>').js_function_call( 'larch.controls.initForm').with_event_handler( 'form_submit', self.submit) p = p.use_js('/static/jquery/js/jquery.form.min.js').use_js( '/static/larch/larch_ui.js').use_css('/static/larch/larch_ui.css') return p
class action_link(CompositePres): def __init__(self, link_text, action_fn=None, css_class=None): """ Create an action link that invokes a function in response to the user clicking it :param link_text: the link text :param action_fn: a callback that is called when the link is clicked, of the form function(event) :param css_class: an optional css class :return: the control """ self.__link_text = link_text self.clicked = EventHandler() if action_fn is not None: self.clicked.connect(action_fn) self.__css_class = css_class def pres(self, pres_ctx): css = ' class="{0}"'.format( self.__css_class) if self.__css_class is not None else '' p = Html('<a {2}href="javascript:" onclick="{0}">{1}</a>'.format( post_event_js_code_for_handler('clicked'), self.__link_text, css)) p = p.with_event_handler('clicked', self.clicked) return p
class text_entry (CompositePres): def __init__(self, text, immediate_events=False, on_edit=None, width=None): """ Create a text entry control :param text: the initial text to display in the control :param immediate_events: if True, an event will be emitted each time the text is edited (on each keypress) :param on_edit: a callback invoked in response to edits, of the form function(modified_text) :param width: width of the control; ints or longs will be interpreted as width in pixels, otherwise use string in CSS form, e.g. '100px', '10em' or '50%' :return: the editor control """ self.edit = EventHandler() if isinstance(width, int) or isinstance(width, long): width = '{0}px'.format(width) self.__text = text self.__immediate_events = immediate_events self.__width = width if on_edit is not None: self.edit.connect(on_edit) self.__channel = MessageChannel() def set_text(self, text): self.__channel.send(text) def pres(self, pres_ctx): sz = '' if self.__width is not None: sz = ' style="width: {0};"'.format(self.__width) p = Html('<input type="text" value="{0}"{1}></input>'.format(self.__text, sz)).js_function_call('larch.controls.initTextEntry', self.__immediate_events, self.__channel) p = p.with_event_handler('text_entry_edit', lambda event: self.edit(event, event.data)) p = p.use_js('/files/static/larch/larch_ui.js').use_css('/files/static/larch/larch_ui.css') return p
class ckeditor(CompositePres): def __init__(self, text, immediate_events=False, use_edit_button=False, config=None, on_edit=None, on_focus=None, on_blur=None): """ Create a ckEditor based rich text editor control :param text: The text to display in the editor :param immediate_events: If true, an event is emitted on each edit (key press) :param use_edit_button: If true, the user must click an edit button to make the text editable :param config: configuration options; see ckEditor documentation :param on_edit: a callback invoked in response to edits, of the form function(event, modified_html_text) :param on_focus: a callback invoked when the editor receives focus; of the form function(event) :param on_blur: a callback invoked when the editor loses focus; of the form function(event) :return: the ckEditor control """ if text == '': text = '<p></p>' if config is None: config = {} self.__text = text self.__config = config self.__immediate_events = immediate_events self.__use_edit_button = use_edit_button self.edit = EventHandler() self.focus = EventHandler() self.blur = EventHandler() if on_edit is not None: self.edit.connect(on_edit) if on_focus is not None: self.focus.connect(on_focus) if on_blur is not None: self.blur.connect(on_blur) self.__channel = MessageChannel() def set_text(self, text): self.__channel.send(text) def pres(self, pres_ctx): if self.__use_edit_button: p = Html( u'<div class="__larch_ui_ckeditor_edit_container"><div>{text}</div></div>' .format(text=self.__text)).js_function_call( 'larch.controls.initCKEditorWithEditButton', self.__config, self.__immediate_events, self.__channel) else: p = Html(u'<div contenteditable="true">{text}</div>'.format( text=self.__text)) p = p.js_function_call('larch.controls.initCKEditor', self.__config, self.__immediate_events, self.__channel) p = p.js_shutdown_function_call('larch.controls.shutdownCKEditor') p = p.use_js('/static/ckeditor/ckeditor.js') p = p.use_js('/static/larch/larch_ui.js').use_css( '/static/larch/larch_ui.css') p = p.with_event_handler('ckeditor_edit', lambda event: self.edit(event, event.data)) p = p.with_event_handler('ckeditor_focus', self.focus) p = p.with_event_handler('ckeditor_blur', self.blur) return p
class code_mirror(CompositePres): def __init__(self, text, immediate_events=False, config=None, on_edit=None, on_focus=None, on_blur=None, modes=None): """ Create a CodeMirror based code editor control :param text: the initial text to display in the control :param immediate_events: if True, an event will be emitted each time the text is edited (on each keypress) :param config: configuration options (see CodeMirror documentation) :param on_edit: a callback invoked in response to edits, of the form function(event, modified_text) :param on_focus: a callback invoked when the editor receives focus; of the form function(event) :param on_blur: a callback invoked when the editor loses focus; of the form function(event) :param modes: a list of names of language plugins to load (e.g. 'python', 'javascript', 'glsl', etc; see CodeMirror documentation) :return: the editor control """ if config is None: config = {} if modes is None: modes = [] self.__text = text self.__immediate_events = immediate_events self.__config = config self.edit = EventHandler() self.focus = EventHandler() self.blur = EventHandler() if on_edit is not None: self.edit.connect(on_edit) if on_focus is not None: self.focus.connect(on_focus) if on_blur is not None: self.blur.connect(on_blur) self.__modes = modes self.__channel = MessageChannel() def set_text(self, text): self.__channel.send(text) def pres(self, pres_ctx): textarea = Html(u'<textarea>{text}</textarea>'.format( text=Html.escape_str(self.__text))) textarea = textarea.js_function_call('larch.controls.initCodeMirror', self.__config, self.__immediate_events, self.__channel) p = Html('<div>', textarea, '</div>') p = p.use_css('/static/codemirror-3.14/lib/codemirror.css') p = p.use_js('/static/codemirror-3.14/lib/codemirror.js') for mode in self.__modes: p = p.use_js( '/static/codemirror-3.14/mode/{0}/{0}.js'.format(mode)) for addon in _addon_js: p = p.use_js('/static/codemirror-3.14/addon/{0}'.format(addon)) for addon in _addon_css: p = p.use_css('/static/codemirror-3.14/addon/{0}'.format(addon)) if _code_mirror_theme is not None: p = p.use_css('/static/codemirror-3.14/theme/{0}.css'.format( _code_mirror_theme)) p = p.use_js('/static/larch/larch_ui.js').use_css( '/static/larch/larch_ui.css') p = p.with_event_handler('code_mirror_edit', lambda event: self.edit(event, event.data)) p = p.with_event_handler('code_mirror_focus', self.focus) p = p.with_event_handler('code_mirror_blur', self.blur) return p
class range_slider (CompositePres): def __init__(self, release_fn=None, slide_fn=None, width=None, values=None, min=None, max=None, step=None, orientation=None, animate=False, disabled=False): """ Create a JQuery UI slider - with the range option enabled :param release_fn: a function to be invoked when the user releases the slider, of the form function(event, value) :param slide_fn: a function to be invoked when the user drags the slider, of the form function(event, value) :param width: the width of the slider, specified as a CSS value e.g. 200px (200 pixels) or 50%, or as an integer value that will be converted to pixels :param values: a pair of values representing the lower and upper bound :param min: the minimum value :param max: the maximum value :param step: the size of steps between positions on the slider :param orientation: either 'horizontal' or 'vertical' :param animate: if True, or if a numeric value in milliseconds specifying the animation length, this will cause the slider to animate when the user clicks to position it directly :param disabled: if True, causes the slider to appear disabled :return: the slider control """ self.release = EventHandler() self.slide = EventHandler() if release_fn is not None: self.release.connect(release_fn) if slide_fn is not None: self.slide.connect(slide_fn) self.__channel = MessageChannel() self.__width = '{0}px'.format(width) if isinstance(width, int) or isinstance(width, long) else width options = {} if values is not None: options['values'] = values options['range'] = True if min is not None: options['min'] = min if max is not None: options['max'] = max if step is not None: options['step'] = step if orientation is not None: options['orientation'] = orientation if animate is not False: options['animate'] = animate if disabled: options['disabled'] = True self.__options = options def set_values(self, values): self.__channel.send(values) def pres(self, pres_ctx): if self.__width is None: div = Html('<div></div>') else: div = Html('<div style="width: {0};"></div>'.format(self.__width)) div = div.js_function_call('larch.controls.initRangeSlider', True, self.__options, self.__channel) div = div.with_event_handler("slider_change", lambda event: self.release(event, event.data)) div = div.with_event_handler("slider_slide", lambda event: self.slide(event, event.data)) div = div.use_js('/static/larch/larch_ui.js').use_css('/static/larch/larch_ui.css') return div
class range_slider (CompositePres): def __init__(self, release_fn=None, slide_fn=None, width=None, values=None, min=None, max=None, step=None, orientation=None, animate=False, disabled=False): """ Create a JQuery UI slider - with the range option enabled :param release_fn: a function to be invoked when the user releases the slider, of the form function(event, value) :param slide_fn: a function to be invoked when the user drags the slider, of the form function(event, value) :param width: the width of the slider, specified as a CSS value e.g. 200px (200 pixels) or 50%, or as an integer value that will be converted to pixels :param values: a pair of values representing the lower and upper bound :param min: the minimum value :param max: the maximum value :param step: the size of steps between positions on the slider :param orientation: either 'horizontal' or 'vertical' :param animate: if True, or if a numeric value in milliseconds specifying the animation length, this will cause the slider to animate when the user clicks to position it directly :param disabled: if True, causes the slider to appear disabled :return: the slider control """ self.release = EventHandler() self.slide = EventHandler() if release_fn is not None: self.release.connect(release_fn) if slide_fn is not None: self.slide.connect(slide_fn) self.__channel = MessageChannel() self.__width = '{0}px'.format(width) if isinstance(width, int) or isinstance(width, long) else width options = {} if values is not None: options['values'] = values options['range'] = True if min is not None: options['min'] = min if max is not None: options['max'] = max if step is not None: options['step'] = step if orientation is not None: options['orientation'] = orientation if animate is not False: options['animate'] = animate if disabled: options['disabled'] = True self.__options = options def set_values(self, values): self.__channel.send(values) def pres(self, pres_ctx): if self.__width is None: div = Html('<div></div>') else: div = Html('<div style="width: {0};"></div>'.format(self.__width)) div = div.js_function_call('larch.controls.initRangeSlider', True, self.__options, self.__channel) div = div.with_event_handler("slider_change", lambda event: self.release(event, event.data)) div = div.with_event_handler("slider_slide", lambda event: self.slide(event, event.data)) div = div.use_js('/files/static/larch/larch_ui.js').use_css('/files/static/larch/larch_ui.css') return div