def editUI(self, controlFactoryFn): self.__incr.onAccess() valueControl = controlFactoryFn(self._live) if self._expr is None: def _onAdd(button, event): self.expr = EmbeddedPython2Expr() addButton = Button(self._addButtonContents, _onAdd).alignHPack() return Row([valueControl, Spacer(10.0, 0.0), addButton]) else: def _onRemove(button, event): self.expr = None removeButton = Button(self._removeButtonContents, _onRemove).alignHPack() return Column([ valueControl, Row([ removeButton, Spacer(10.0, 0.0), exprBorder.surround(self._expr) ]) ])
def __present__(self , fragment, inherited_state): unload_modules_starting_with(['controls', 'datamodel', 'mallard']) from datamodel import xmlmodel from mallard import mallard title = _info_style(TitleBar(self._filename)) xml_title = _section_heading_style(SectionHeading1('XML (non-editable)')).alignHExpand() rich_text_title = _section_heading_style(SectionHeading1('Rich text (editable)')).alignHExpand() if self.__xml_path is not None: with open(self.__xml_path, 'r') as f: xml_rep = xmlmodel.XmlElem.from_file(f) elif self.__xml_bytes is not None: xml_rep = xmlmodel.XmlElem.from_string(self.__xml_bytes) else: raise ValueError, 'Must provide either path or bytes' rich_text_rep = mallard.edit(xml_rep) scrolled_xml = ScrolledViewport(Pres.coerce(xml_rep).alignVRefY(), 100.0, 400.0, None) scrolled_rich_text = ScrolledViewport(_editable_style(rich_text_rep).alignVRefY(), 100.0, 400.0, None) xml_contents = Column([xml_title.alignVRefY(), Spacer(0.0, 20.0), scrolled_xml]) rich_text_contents = Column([rich_text_title.alignVRefY(), Spacer(0.0, 20.0), scrolled_rich_text]) xml_sec = _section_border.surround(xml_contents).pad(2.0, 2.0) rich_text_sec = _section_border.surround(rich_text_contents).pad(2.0, 2.0) contents = Row([xml_sec.alignHExpand(), rich_text_sec.alignHExpand()]) return _info_style(Page([title.alignVRefY(), contents])).alignVExpand()
def __present__(self, fragment, inheritedState): configurationLink = Hyperlink( 'CONFIGURATION PAGE', fragment.subject.world.configuration.subject()) linkHeader = LinkHeaderBar([configurationLink]) title = TitleBar('About') splash = Image.systemImage('SplashScreen.png') desc = NormalText( 'The Larch Environment was designed and written by Geoffrey French' ) jythonLink = Hyperlink('Jython', URI('http://www.jython.org/')) jerichoLink = Hyperlink('Jericho HTML parser', URI('http://jericho.htmlparser.net')) salamanderLink = Hyperlink('SVG Salamander', URI('http://svgsalamander.java.net/')) googleFontsLink = Hyperlink('Google Fonts', URI('http://www.google.com/fonts')) jythonAcks = NormalText([ 'The Larch Environment incorporates the ', jythonLink, ' interpreter.' ]) libraryAcks = NormalText([ 'Larch incorporates the ', jerichoLink, ', and ', salamanderLink, ' libraries.' ]) fontAcks = NormalText([ 'Larch incorporates the following fonts (found at ', googleFontsLink, '): ', 'Arimo (by Steve Matteson), Lora (by Cyreal), Nobile (by Vernon Adams), Noto Sans (by Google), ', 'Open Sans (by Steve Matteson), PT Serif (by ParaType), and Source Sans Pro (by Paul D. Hunt)' ]) copyright = NormalText('(C) copyright Geoffrey French 2008-2013') homePage = Hyperlink('The Larch Environment website', URI('http://www.larchenvironment.com')) head = Head([linkHeader, title]) body = Body([ splash.padY(20.0).alignHCentre(), desc.padY(10.0).alignHCentre(), Spacer(0.0, 10.0), jythonAcks.pad(25.0, 5.0).alignHLeft(), libraryAcks.pad(25.0, 5.0).alignHLeft(), fontAcks.pad(25.0, 5.0).alignHLeft(), Spacer(0.0, 10.0), Row([copyright.alignHLeft(), homePage.alignHRight()]).pad(10.0, 10.0).alignHExpand() ]) return StyleSheet.style(Primitive.editable(False)).applyTo( Page([head, body]))
def __present_contents__(self, fragment, inheritedState): def _onApply(button, event): self._config = self._userConfig.getStaticValue() self.apply() def _onRevert(button, event): self._userConfig.setLiteralValue(self._config) choices = [Label(c[1]) for c in _fontConfigChoices] choices.append(Label('Custom')) @LiveFunction def indexOfChoice(): config = self._userConfig.getValue() if isinstance(config, FontConfiguration): return len(choices) - 1 else: try: return [c[0] for c in _fontConfigChoices ].index(self._userConfig.getValue()) except ValueError: return 0 @LiveFunction def footer(): config = self._userConfig.getValue() if isinstance(config, FontConfiguration): return config else: return Blank() def _onChoice(menu, prevChoice, choice): if choice == len(choices) - 1: # Custom if prevChoice != choice: prevConfig = _fontConfigChoices[prevChoice][0] prevConfig = _fontConfigForName(prevConfig) config = FontConfiguration() config.copyFrom(prevConfig) self._userConfig.setLiteralValue(config) else: self._userConfig.setLiteralValue(_fontConfigChoices[choice][0]) applyButton = Button.buttonWithLabel('Apply', _onApply) revertButton = Button.buttonWithLabel('Revert', _onRevert) buttons = Row([applyButton, Spacer(15.0, 0.0), revertButton]).alignHPack() configMenu = OptionMenu(choices, indexOfChoice, _onChoice) chooserHeader = Row([ Label('Choose a font configuration:'), Spacer(25.0, 0.0), configMenu ]).alignHPack() return self._pageColumnStyle(Column([buttons, chooserHeader, footer]))
def __present__(self, fragment, inheritedState): title = SectionHeading2( 'Unit tests' ) nameEntry = _nameBorder.surround( EditableLabel( self._name, _notSet ).regexValidated( Tokens.identifierPattern, 'Please enter a valid identifier' ) ) header = Row( [ title, Spacer( 25.0, 0.0 ), nameEntry ] ) if self._tableEditor is NotImplemented: raise NotImplementedError, 'Table editor is abstract' table = self._tableEditor.editTable( self._tests ) return _inlineTestTableBorder.surround( Column( [ header, Spacer( 0.0, 5.0 ), table ] ) )
def __present__(self, fragment, inheritedState): def _onClick(button, event): self._stepper._onRun() return Button( Row([_forwardArrow, Spacer(2.0, 0.0), _forwardArrow]).pad(2.0, 2.0), _onClick).alignVCentre()
def paddingUI(): padding = self.padding.value if padding is None: def onPadUniform(button, event): self.padding.value = UniformPadding() def onPadNonUniform(button, event): self.padding.value = NonUniformPadding() return ControlsRow([ Button.buttonWithLabel('Uniform', onPadUniform), Button.buttonWithLabel('Non-uniform', onPadNonUniform) ]).alignHPack() else: def onRemove(button, event): self.padding.value = None def setPadding(padding): self.padding.value = padding removeButton = Button.buttonWithLabel('Remove padding', onRemove).alignHPack() return Column([ removeButton.alignHPack(), Spacer(0.0, 5.0), padding.editUI(setPadding) ])
class GUIEditorRootComponent(GUIUnaryBranchComponent): isRootGUIEditorComponent = True def __init__(self, contents=None): super(GUIEditorRootComponent, self).__init__(child=contents) self._guiEditor = None @property def guiEditor(self): return self._guiEditor def __component_py_evalmodel__(self, codeGen): child = self.child if child is not None: return child.__py_evalmodel__(codeGen) else: blank = codeGen.embeddedValue(Blank) return Py.Call(target=blank, args=[]) def __present__(self, fragment, inheritedState): p = self._presentChild() p = p.withContextMenuInteractor(componentContextMenu) p = p.withProperty(GUICProp.instance, self) return p _emptyPres = Spacer(15.0, 15.0)
def __present__(self, fragment, inheritedState): self._listeners = PresentationStateListenerList.addListener( self._listeners, fragment) name = Label(self._name) kind = _objectKindMap[self._kind] contents = Column([name, Spacer(0.0, 2.0), kind.padX(10.0, 0.0)]) return _fragSelectorEntryBorder.surround( contents).withElementInteractor(self._interactor)
def __present__(self, fragment, inheritedState): self._incr.onAccess() exprPres = self._expr header = Row([ self._angleQuoteStyle(Label(u'\u00ab')), self._angleQuoteStyle(Label(u'\u00bb')), Spacer(9.0, 0.0) ]).withDragSource(_dragSource) return ObjectBorder(Row([header.alignVCentre(), exprPres]))
def __present__(self, fragment, inheritedState): current = self._isCurrent.getValue() label = _breakPointStyle(Label('BREAKPOINT')) if current: return _breakPointCurrentBorder.surround( Row([_breakPointArrow, Spacer( 3.0, 0.0, ), label]).alignVCentre()) else: return _breakPointBorder.surround(label)
def _collapsibleFontChooser(title, choiceValue, sampleFn): expanded = LiveValue(False) @LiveFunction def currentChoice(): return _headerNameStyle(Label(choiceValue.getValue())) header = Row([Label(title), Spacer(25.0, 0.0), currentChoice]).alignHPack() return DropDownExpander( header, ScrolledViewport(_fontChooser(choiceValue, sampleFn), 800.0, 400.0, None), expanded).alignHExpand()
def __present__(self , fragment, inherited_state): title = TitleBarWithSubtitle('Mallard editor', 'development prototype - SAVE FUNCTIONALITY NOT IMPLEMENTED') path_heading = SectionHeading2(['Displaying contents of: ', _dir_style(RichSpan(self.__path))]) links = [Hyperlink(p.filename, editor_page.EditorPageSubject(p, fragment.subject)) for p in self._pages] if self._example_pages is not None: examples_heading = SectionHeading2('Example mallard documents:') example_links = [Hyperlink(p.filename, editor_page.EditorPageSubject(p, fragment.subject)) for p in self._example_pages] examples_section = [Spacer(0.0, 20.0), examples_heading, Column(example_links)] else: examples_section = [] return _page_style(Page([title, path_heading, Column(links)] + examples_section))
def editor(): x = live.getValue() if x is None: def on_add(button, event): live.setLiteralValue(initialValue) return Button(_plusStyle(Label('+')), on_add).alignHPack() else: def on_delete(button, event): live.setLiteralValue(None) deleteButton = Button(Image.systemIcon('delete'), on_delete) return Row([deleteButton.alignHPack(), Spacer(5.0, 0.0).alignHPack(), valueEditor])
def samplePage(): label = Label('Sample page:') title = titleSample(self._title.getValue(), 'Example Page Title') heading = headingSample(self._heading.getValue(), 'Main heading') normal1 = normalSample(self._normal.getValue(), 'Normal text will appear like this.') normal2 = normalSample( self._normal.getValue(), 'Paragraphs of normal text are used for standard content.') ui1 = uiHeadingSample(self._uiHeading.getValue(), 'UI heading') genericLabel = Label( 'Generic text (within controls, code, etc) will appear like this.' ) buttons = self._buttonRowStyle( Row([ Button.buttonWithLabel('Button %d' % (i, ), None) for i in xrange(0, 5) ])) ui = Section(ui1, Column([genericLabel, Spacer(0.0, 7.0), buttons])) page = Page([title, Body([heading, normal1, normal2, ui])]) return Column([label, Spacer(0.0, 15.0), page])
def __present__(self, fragment, inheritedState): def _literateExpressionMenu(element, menu): def _onToggleExpanded(item): self.setExpanded(not self._expanded) menuItemName = 'Start as contracted' if self._expanded else 'Start as expanded' menu.add( MenuItem.menuItemWithLabel(menuItemName, _onToggleExpanded)) return False self._incr.onAccess() self._definition._incr.onAccess() suitePres = self._definition._suite nameLabel = self._nameStyle(Label(self._definition._name)) liveName = LiveValue(nameLabel) class _NameEntryListener(TextEntry.TextEntryListener): def onAccept(listener, textEntry, text): self.setName(text) def onCancel(listener, textEntry, orignalText): liveName.setLiteralValue(nameLabel) def _onNameButton(button, event): nameEntry = TextEntry(self._definition._name, _NameEntryListener()) nameEntry.grabCaretOnRealise() nameEntry = self._nameStyle(nameEntry) liveName.setLiteralValue(nameEntry) renameButton = self._nameButtonStyle( Button.buttonWithLabel('...', _onNameButton)) header = Row([ self._angleQuoteStyle(Label(u'\u00ab')), liveName, self._angleQuoteStyle(Label(u'\u00bb')), Spacer(10.0, 0.0), renameButton ]).withDragSource(_dragSource) dropDown = self._dropDownStyle( DropDownExpander(header, suitePres, self._expanded, None)) return ObjectBorder(dropDown).withContextMenuInteractor( _literateExpressionMenu)
def __present__(self, fragment, inheritedState): self._incr.onAccess() title = SectionHeading2( 'Unit tests' ) nameEntry = _nameBorder.surround( EditableLabel( self._name, _notSet ).regexValidated( Tokens.identifierPattern, 'Please enter a valid identifier' ) ) header = Row( [ title, Spacer( 25.0, 0.0 ), nameEntry ] ) contents = [ header.padY( 0.0, 5.0 ), _standardCodeBorder.surround( self._suite ).padY( 3.0 ).alignHExpand() ] if self.__passes is not None and self.__failures is not None: resultsTitle = SectionHeading3( 'Test results:' ) passes = _standardPassStyle( Label( '%d / %d test(s) passed' % ( self.__passes, self.__passes + len( self.__failures ) ) ) ) failuresLabel = _standardFailStyle( Label( '%d test(s) failed:' % len( self.__failures ) ) ) failures = [ Column( [ _standardFailedTestStyle( Label( name ) ), Pres.coerce( exception ).padX( 5.0, 0.0 ) ] ).padX( 5.0, 0.0 ) for name, exception in self.__failures ] results = _standardResultsBorder.surround( Column( [ resultsTitle, passes, failuresLabel ] + failures ) ).pad( 3.0, 3.0 ) contents.append( results ) return _standardInlineTestBorder.surround( Column( contents ) )
def buildEditorPres(self, fragment, inheritedState): def _onOpen(button, event): component = button.getElement().getRootElement().getComponent() fileChooser = JFileChooser() self._initFileChooser(fileChooser) response = fileChooser.showDialog(component, 'Open') if response == JFileChooser.APPROVE_OPTION: sf = fileChooser.getSelectedFile() if sf is not None: filename = sf.getPath() if filename is not None: self._model.liveValue.setLiteralValue(filename) openButton = Button.buttonWithLabel('...', _onOpen) return Row([ openButton, Spacer(3.0, 0.0), EditableLabel(self._model.liveValue, _notSetStyle(Label('<none>'))).regexValidated( _filenamePattern, 'Please enter a valid filename') ])
def _presentDir(self): def _onSet(hyperlink, event): component = hyperlink.getElement().getRootElement().getComponent() openDialog = JFileChooser() openDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY) response = openDialog.showDialog(component, 'Choose path') if response == JFileChooser.APPROVE_OPTION: sf = openDialog.getSelectedFile() if sf is not None: filename = sf.getPath() if filename is not None and os.path.isdir(filename): self._graphVizDir = filename self._refreshConfig() self._incr.onChanged() dirLabel = Label( self._graphVizDir ) if self._graphVizDir is not None else self._notSetStyle.applyTo( Label('<Not set>')) setLink = Hyperlink('CHANGE', _onSet) return self._dirBorderStyle.applyTo( Border(Row([dirLabel, Spacer(25.0, 0.0), setLink])))
def _presentLeafContents(self, fragment, inheritedState): return Spacer(self.width.getValueForEditor(), self.height.getValueForEditor())
def ProjectRoot(self, fragment, inheritedState, project): # Save and Save As def _onSave(control, buttonEvent): if document.hasFilename(): document.save() else: def handleSaveDocumentAsFn(filename): document.saveAs(filename) DocumentManagement.promptSaveDocumentAs( world, control.getElement().getRootElement().getComponent(), handleSaveDocumentAsFn) def _onSaveAs(control, buttonEvent): def handleSaveDocumentAsFn(filename): document.saveAs(filename) DocumentManagement.promptSaveDocumentAs( world, control.getElement().getRootElement().getComponent(), handleSaveDocumentAsFn, document.getFilename()) def _onReload(control, buttonEvent): if document.hasFilename(): document.save() document.reload() project.reset() else: def handleSaveDocumentAsFn(filename): document.saveAs(filename) document.reload() project.reset() DocumentManagement.promptSaveDocumentAs( world, control.getElement().getRootElement().getComponent(), handleSaveDocumentAsFn) def _onExport(control, event): component = control.getElement().getRootElement().getComponent() openDialog = JFileChooser() openDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY) response = openDialog.showDialog(component, 'Export') if response == JFileChooser.APPROVE_OPTION: sf = openDialog.getSelectedFile() if sf is not None: filename = sf.getPath() if filename is not None and os.path.isdir(filename): response = JOptionPane.showOptionDialog( component, 'Existing content will be overwritten. Proceed?', 'Overwrite existing content', JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, None, ['Overwrite', 'Cancel'], 'Cancel') if response == JFileChooser.APPROVE_OPTION: exc = None try: project.export(filename) except: exc = JythonException.getCurrentException() if exc is not None: BubblePopup.popupInBubbleAdjacentTo( DefaultPerspective.instance(exc), control.getElement(), Anchor.BOTTOM, True, True) # Python package name class _PythonPackageNameListener(EditableLabel.EditableLabelListener): def onTextChanged(self, editableLabel, text): if text != '': project.pythonPackageName = text else: project.pythonPackageName = None # Project index def _addPackage(menuItem): project.append(ProjectPackage('NewPackage')) def _addPage(page): project.append(page) def _projectIndexContextMenuFactory(element, menu): menu.add(MenuItem.menuItemWithLabel('New package', _addPackage)) newPageMenu = PageData.newPageMenu(_addPage) importPageMenu = PageData.importPageMenu( element.getRootElement().getComponent(), _addPage) menu.add( MenuItem.menuItemWithLabel( 'New page', newPageMenu, MenuItem.SubmenuPopupDirection.RIGHT)) menu.add( MenuItem.menuItemWithLabel( 'Import page', importPageMenu, MenuItem.SubmenuPopupDirection.RIGHT)) return True # Get some initial variables document = fragment.subject.document world = fragment.subject.world # Title title = TitleBar(document.getDocumentName()) # Controls for 'save' and 'save as' saveExportHeader = SectionHeading1('Save/export') saveButton = Button.buttonWithLabel('Save', _onSave) saveAsButton = Button.buttonWithLabel('Save as', _onSaveAs) reloadButton = Button.buttonWithLabel('Save and reload', _onReload) reloadButton = AttachTooltip( reloadButton, 'Saves and reloads the project from scratch\nCauses all embedded objects to be re-created.' ) exportButton = Button.buttonWithLabel('Export', _onExport) exportButton = AttachTooltip( exportButton, 'Exports project contents to text files where possible.') saveBox = Row([ saveButton.padX(10.0), Spacer(30.0, 0.0), saveAsButton.padX(10.0), Spacer(30.0, 0.0), reloadButton.padX(10.0), Spacer(50.0, 0.0), exportButton.padX(10.0) ]).alignHLeft() saveExportSection = Section(saveExportHeader, saveBox) # # Project Section # # Python package name notSet = _pythonPackageNameNotSetStyle.applyTo(Label('<not set>')) pythonPackageNameLabel = EditableLabel( project.pythonPackageName, notSet, _PythonPackageNameListener()).regexValidated( _pythonPackageNameRegex, 'Please enter a valid dotted identifier') pythonPackageNameLabel = AttachTooltip( pythonPackageNameLabel, 'The root python package name is the name under which the contents of the project can be imported using import statements within the project.\n' + \ 'If this is not set, pages from this project cannot be imported.', False ) pythonPackageNameRow = Form.Section( 'Root Python package name', 'Pages will not be importable unless this is set', pythonPackageNameLabel) # Clear imported modules def _onReset(button, event): project.reset() modules = document.unloadAllImportedModules() heading = SectionHeading2('Unloaded modules:') modules = Column([Label(module) for module in modules]) report = Section(heading, modules) BubblePopup.popupInBubbleAdjacentTo(report, button.getElement(), Anchor.BOTTOM, True, True) resetButton = Button.buttonWithLabel('Reset', _onReset) resetButton = AttachTooltip( resetButton, 'Unloads all modules that were imported from this project from the Python module cache. This way they can be re-imported, allowing modifications to take effect.' ) resetRow = Form.Section('Reset', 'Unload project modules', resetButton) projectSection = Form('Project', [pythonPackageNameRow, resetRow]) # Project index indexHeader = SectionHeading1('Index') nameElement = _projectIndexNameStyle.applyTo(Label('Project root')) nameBox = _itemHoverHighlightStyle.applyTo(nameElement.alignVCentre()) nameBox = nameBox.withContextMenuInteractor( _projectIndexContextMenuFactory) nameBox = _ProjectTreeController.instance.item(project, nameBox) nameBox = AttachTooltip( nameBox, 'Right click to access context menu, from which new pages and packages can be created.\n' + \ 'A page called index at the root will appear instead of the project page. A page called __startup__ will be executed at start time.', False ) itemsBox = Column(project[:]).alignHExpand() itemsBox = _ProjectTreeController.instance.editableList( project, itemsBox) contentsView = Column([ nameBox.alignHExpand(), itemsBox.padX(_packageContentsIndentation, 0.0).alignHExpand() ]) indexSection = Section(indexHeader, contentsView) def _onBuildJar(button, event): _buildProjectJar(button.element, document) buildJarButton = Button.buttonWithLabel('Build JAR', _onBuildJar) jarRow = Form.Section('Build executable app', 'Export the project as an executable JAR', buildJarButton) packagingSection = Form('Packaging', [jarRow]) indexTip = TipBox([ NormalText([ StrongSpan('Index: '), 'Larch projects act like Python programs. Packages act as directories/packages and pages act as Python source files. Pages can import code from one another as if they are modules.' ]), NormalText([ 'New pages and packages can be created by right clicking on the entries in the index or on ', EmphSpan('Project root'), ' (they will highlight as you hover over them).' ]), NormalText([ StrongSpan('Front and startup pages: '), 'If a page is set as the front page it will appear instead of the project page. In these cases, the project page can still be reached using the links in the location bar at the top of the window.' ]), 'If a page is set as the startup page, code within it will be executed before all other pages. This can be used for registering editor extensions.', 'To set a page as the front page or the startup page, right-click on it to show its context menu and choose the appropriate option.' ], 'larchcore.worksheet.worksheeteditor') # The page head = Head([title]) body = Body([ saveExportSection, projectSection, indexSection, packagingSection, indexTip ]).alignHPack() return StyleSheet.style(Primitive.editable(False)).applyTo( Page([head, body]))
def __present__(self, fragment, inheritedState): self._incr.onAccess() valuesLive = LiveValue( Blank() ) tree = Column( [ x._presentFrameSubtree( valuesLive ) for x in self._rootFrames ] ) return Column( [ tree, Spacer( 0.0, 10.0 ), valuesLive ] )