def getGlyphs(self): glyphs = [] # font window selection if self.w.selectedGlyphs.get(): font = CurrentFont() if font is not None: glyphs += font.selectedGlyphs # current glyph window if self.w.currentGlyph.get(): glyphWindow = CurrentGlyphWindow() if glyphWindow is not None: g = glyphWindow.getGlyph() if g is not None and g not in glyphs: glyphs += [RGlyph(g)] # space center selection if self.w.spaceCenter.get(): spaceCenter = CurrentSpaceCenter(currentFontOnly=True) if spaceCenter is not None: g = spaceCenter.glyphLineView.getSelected() if g is not None and g not in glyphs: glyphs += [RGlyph(g)] return glyphs
def controlChanged(self, info): # Make sure the RoboControlInput notificaiton is for the desired control name: if info["name"] == self.controlName: # Figure out some info about the current glyph, and current glyph window glyph = CurrentGlyph() font = CurrentFont() glyphOrder = [] if font: glyphOrder = font.lib["public.glyphOrder"] # If there's a glyph window open: w = CurrentGlyphWindow() if w: # Find this glyph's index in the glyphOrder thisGlyphIndex = glyphOrder.index(glyph.name) prevGlyphIndex = thisGlyphIndex - 1 nextGlyphIndex = thisGlyphIndex + 1 if nextGlyphIndex == len(glyphOrder): nextGlyphIndex = 0 elif prevGlyphIndex < 0: prevGlyphIndex = len(glyphOrder) - 1 # Then, find the previous/next glyph names prevGlyphName = glyphOrder[prevGlyphIndex] nextGlyphName = glyphOrder[nextGlyphIndex] # Now that we know something about the current glyph and its neighbors: if info["value"] == "cw": # Move clockwise, next glypyh w.setGlyphByName(nextGlyphName) elif info["value"] == "ccw": # Counter-clockwise, prev glyph w.setGlyphByName(prevGlyphName)
def _callback_goto_glyph(self, sender=None): newGlyphName = sender.get()[sender.getSelection()[0]]["Name"] if CurrentGlyphWindow(): CurrentGlyphWindow().setGlyphByName(newGlyphName) else: # TODO: open glyph window? pass
def show_dist_textbox(self, info): window = CurrentGlyphWindow() view = window.getGlyphView() vanillaView = ShowDistTextBox(view, (20, 22, 120, 22), "", alignment="left", sizeStyle="mini") window.addGlyphEditorSubview(vanillaView)
def _glyphset_List_doubleClickCallback(self, sender): sel = sender.getSelection() if not sel: return if self.ui.glyph is None: return OpenGlyphWindow(self.ui.glyph) gw = CurrentGlyphWindow() appearance = NSAppearance.appearanceNamed_( 'NSAppearanceNameVibrantDark') gw.window().getNSWindow().setAppearance_(appearance)
def getActiveGlyphWindow(): window = CurrentGlyphWindow() # there is no glyph window if window is None: return None # the editor is not the first responder if not window.getGlyphView().isFirstResponder(): return None return window
def glyphChanged(self, info): # Reset the glyph info self.glyph0.clear() self.glyph1.clear() self.compatibilityReport = None self.window = CurrentGlyphWindow() self.interpolatedGlyph.clear() # Remove any observers on the older CurrentGLyph and add them to the new one if self.currentGlyph: self.currentGlyph.removeObserver(self, "Glyph.Changed") self.currentGlyph.removeObserver(self, "Glyph.ContoursChanged") self.currentGlyph = CurrentGlyph() if self.currentGlyph: self.currentGlyph.addObserver(self, "optionsChanged", "Glyph.Changed") self.currentGlyph.addObserver(self, "optionsChanged", "Glyph.ContoursChanged") if not self.currentGlyph == None: # Update the glyph info glyphName = self.currentGlyph.name master0idx = self.w.font0.get() master1idx = self.w.font1.get() master0 = self.fonts[master0idx] master1 = self.fonts[master1idx] if glyphName in master0: self.glyph0.clear() pen = DecomposingPen(master0, self.glyph0.getPen()) master0[glyphName].draw(pen) if glyphName in master1: self.glyph1.clear() pen = DecomposingPen(master1, self.glyph1.getPen()) master1[glyphName].draw(pen) # Update the interp compatibility report self.testCompatibility() # Adjust the frame of the window to fit the interpolation # (Thanks Frederik!) if self.window: widths = [] if self.glyph0: widths.append(self.glyph0.width) if self.glyph1: widths.append(self.glyph1.width) if len(widths): widths.sort() view = self.window.getGlyphView() scale = view.scale() (x, y), (w, h) = view.frame() ox, oy = view.offset() extraWidth = widths[-1] * scale view.setOffset((ox, oy)) view.setFrame_(((x, y), (w + extraWidth, h))) # Update the view self.optionsChanged(None)
def controlChanged(self, info): # Check that the control that changed has the name were looking for: if info["name"] == self.controlName: # If there's a glyph window open w = CurrentGlyphWindow() if w: # Set the zoom scale of the window. # Scale the incoming "value" of the control to somewhere between 0.15% and 3000% scale = (info["value"] * 30) + 0.15 w.setGlyphViewScale(scale)
def setDarkMode(w, darkMode): if darkMode: appearance = NSAppearance.appearanceNamed_('NSAppearanceNameVibrantDark') if hasattr(w, "accordionView"): w.accordionView.setBackgroundColor(NSColor.colorWithCalibratedRed_green_blue_alpha_(.08, .08, .08, 1)) else: appearance = NSAppearance.appearanceNamed_('NSAppearanceNameAqua') if hasattr(w, "accordionView"): w.accordionView.setBackgroundColor(NSColor.colorWithCalibratedRed_green_blue_alpha_(1, 1, 1, 1)) w.getNSWindow().setAppearance_(appearance) if CurrentGlyphWindow(): CurrentGlyphWindow().window().getNSWindow().setAppearance_(appearance)
def drawPoints(self, info): newPoints = self.getValues(None) if newPoints != None: glyph = CurrentGlyph() onCurveSize = getDefault("glyphViewOncurvePointsSize") * 5 offCurveSize = getDefault("glyphViewOncurvePointsSize") * 3 fillColor = tuple([i for i in getDefault("glyphViewCurvePointsFill")]) upmScale = (glyph.font.info.unitsPerEm/1000) scale = info["scale"] oPtSize = onCurveSize * scale fPtSize = offCurveSize * scale d.save() # thanks Erik! textLoc = newPoints[0][3][0] + math.cos(self.returnAngle(newPoints)) * (scale*.25*120) * 2, newPoints[0][3][1] + math.sin(self.returnAngle(newPoints)) * (scale*.25*120) * 2 for b in newPoints: for a in b: if a == newPoints[1][0] or a == newPoints[0][-1]: d.oval(a[0]-oPtSize/2,a[1]-oPtSize/2, oPtSize, oPtSize) else: d.oval(a[0]-fPtSize/2,a[1]-fPtSize/2, fPtSize, fPtSize) d.fill(fillColor[0],fillColor[1],fillColor[2],fillColor[3]) d.stroke(fillColor[0],fillColor[1],fillColor[2],fillColor[3]) d.strokeWidth(scale) d.line(newPoints[0][2],newPoints[1][1]) d.restore() # https://robofont.com/documentation/building-tools/toolspace/observers/draw-info-text-in-glyph-view/?highlight=draw%20text glyphWindow = CurrentGlyphWindow() if not glyphWindow: return glyphView = glyphWindow.getGlyphView() textAttributes = { AppKit.NSFontAttributeName: AppKit.NSFont.userFixedPitchFontOfSize_(11), } glyphView.drawTextAtPoint( f'{round(abs(math.degrees(self.returnAngle(newPoints)))%180,4)}°\n{str(round(self.returnRatio(newPoints),4))}', textAttributes, textLoc, yOffset=0, drawBackground=True, centerX=True, centerY=True, roundBackground=False,) UpdateCurrentGlyphView()
def update(self): self.glyph_window = CurrentGlyphWindow() if self.glyph_window is not None: self.glyph = CurrentGlyph() self.font = self.glyph.getParent() self.glyph_index = self.font.glyphOrder.index(self.glyph.name) self.font_index = self.all_fonts.index(self.font) self._update_text_box() return True else: f = CurrentFont() if f is not None: self.font = f self.font_index = self.all_fonts.index(self.font) glyph_names = get_glyphs(f) if len(glyph_names) > 0: self.glyph = self.font[glyph_names[0]] self.glyph_index = self.font.glyphOrder.index( self.glyph.name) self.glyph_window = OpenGlyphWindow(self.glyph, newWindow=False) self._update_text_box() return True else: print(no_glyph_selected) return False else: print(no_font_open) return False
def layer_up(self): try: self.glyph_window.layerUp() except AttributeError: self.glyph_window = CurrentGlyphWindow() self.glyph_window.layerUp() self.update()
def __init__(self): glyphWindow = getActiveGlyphWindow() if glyphWindow is None: return self.w = ActionWindow((1, 1), centerInView=CurrentGlyphWindow().getGlyphView()) self.w.responderWillBecomeFirstCallback = self.responderWillBecomeFirstCallback # There is probably a better way to set # the escape key to close the window but # I am lazy so I'm using a hidden button. self.w._closeButton = vanilla.ImageButton( (0, 0, 0, 0), bordered=False, callback=self._closeButtonCallback) self.w._closeButton.bind("\u001B", []) # Build the interface self.metrics = dict( margin=15, iconPadding=5, iconButtonWidth=30, iconButtonHeight=30, groupPadding=15, ) rules = self.buildInterface(self.w) if rules is not None: self.w.addAutoPosSizeRules(rules, self.metrics) # Bind close. self.w.bind("close", self.windowCloseCallback) # Go self.w.open()
def __init__(self, glyph, construction, decompose): self.glyph = glyph width = 350 height = 120 editorWindow = CurrentGlyphWindow() (editorX, editorY, editorW, editorH), screen = getGlyphEditorRectAndScreen(editorWindow) x = editorX + ((editorW - width) / 2) y = editorY + ((editorH - height) / 2) self.w = StatusInteractivePopUpWindow((x, y, width, height), screen=screen) self.w.constructionEditor = vanilla.EditText((15, 15, -15, 22), construction) self.w.decomposeCheckBox = vanilla.CheckBox((15, 45, -15, 22), "Decompose", value=decompose) self.w.open() self.w.line = vanilla.HorizontalLine((15, -45, -15, 1)) self.w.cancelButton = vanilla.Button( (-165, -35, 70, 20), "Cancel", callback=self.cancelButtonCallback) self.w.okButton = vanilla.Button((-85, -35, 70, 20), "OK", callback=self.okButtonCallback) self.w.setDefaultButton(self.w.okButton) self.w.cancelButton.bind(".", ["command"]) self.w.getNSWindow().makeFirstResponder_( self.w.constructionEditor.getNSTextField()) self.w.open()
def glyphWindowWillClose(self, notification): start = time.time() self.roboCJKView.setglyphState(self.currentGlyph) self.openedGlyphName = "" if self.glyphInspectorWindow is not None: self.glyphInspectorWindow.closeWindow() self.glyphInspectorWindow = None try: posSize = CurrentGlyphWindow().window().getPosSize() setExtensionDefault(blackrobocjk_glyphwindowPosition, posSize) self.glyphWindowPosSize = getExtensionDefault( blackrobocjk_glyphwindowPosition) except: pass if self.currentGlyph.type != "atomicElement": self.window.removeGlyphEditorSubview(self.glyphView) self.drawer.refGlyph = None self.drawer.refGlyphPos = [0, 0] self.drawer.refGlyphScale = [1, 1] self.currentFont.fontLib.update(self.currentFont._RFont.lib.asDict()) self.currentFont._fullRFont.lib.update(self.currentFont._RFont.lib) if not self.mysql: self.currentFont.save() if self.currentGlyph is not None: self.currentFont.getGlyph(self.currentGlyph) self.currentFont.clearRFont() else: self.currentFont.saveGlyph(self.currentGlyph) self.currentFont.saveFontlib() self.currentFont.batchUnlockGlyphs([self.currentGlyph.name]) stop = time.time() print(stop - start, "to close %s" % self.currentGlyph.name)
def previous_glyph(self): prev = previous_glyph(self.font, self.glyph_index) try: self.glyph_window.setGlyphByName(prev) except AttributeError: self.glyph_window = CurrentGlyphWindow() self.glyph_window.setGlyphByName(prev) self.update()
def next_glyph(self): next = next_glyph(self.font, self.glyph_index) try: self.glyph_window.setGlyphByName(next) except AttributeError: self.glyph_window = CurrentGlyphWindow() self.glyph_window.setGlyphByName(next) self.update()
def getImageForView(self, viewName): if viewName == "Glyph View": window = CurrentGlyphWindow() if window is None: return None view = window.getGlyphView().enclosingScrollView() elif viewName == "Space Center": window = CurrentSpaceCenter() if window is None: return None view = window.glyphLineView.getNSScrollView() data = self._getImageForView(view) data = data.bytes() if isinstance(data, memoryview): data = data.tobytes() return data
def setInFontCallback(self, sender): if self.w.fontSelection.get() == 0: if CurrentFont() is not None: fonts = [CurrentFont()] else: fonts = [] else: fonts = AllFonts() for f in fonts: with f.undo('italicBowtie'): f.info.italicAngle = self.getItalicAngle() f.lib[self.italicSlantOffsetKey] = self.getItalicSlantOffset() try: window = CurrentGlyphWindow() window.setGlyph(CurrentGlyph().naked()) except Exception: print(self.DEFAULTKEY, 'error resetting window, please refresh it') self.updateBowtie()
def setInFontCallback(self, sender): view = self.getView() if view.fontSelection.get() == 0: if CurrentFont() is not None: fonts = [CurrentFont()] else: fonts = [] else: fonts = AllFonts() for f in fonts: f.prepareUndo() f.info.italicAngle = self.getItalicAngle() f.lib[self.italicSlantOffsetKey] = self.getItalicSlantOffset() f.performUndo() try: window = CurrentGlyphWindow() window.setGlyph(CurrentGlyph().naked()) except: print(self.DEFAULTKEY, 'error resetting window, please refresh it') self.updateView()
def mouseDown(self, info): if info["clickCount"] == 2: if not self.currentGlyph == None: mouseLoc = (info["point"].x, info["point"].y) if not self.previousGlyph == None: # There's a previous glyph being drawn, check to see if the double click is in its bouds if self.inBounds(mouseLoc, self.previousGlyph.bounds, self.currentGlyph.width): cgw = CurrentGlyphWindow() cgw.setGlyphByName(self.previousGlyph.name) else: # Check the components for c in self.currentGlyph.components: if c.selected: # First click should select it if self.inBounds(mouseLoc, c.bounds): self.didSwitch = True cgw = CurrentGlyphWindow() cgw.setGlyphByName(c.baseGlyph)
def getWindowPostition(self): # Code from Tal # https://forum.robofont.com/topic/573/automatic-statusinteractivepopupwindow-positioning if not CurrentGlyphWindow(): return nsWindow = CurrentGlyphWindow().w.getNSWindow() scrollView = CurrentGlyphWindow().getGlyphView().enclosingScrollView() rectInWindowCoords = scrollView.convertRect_toView_(scrollView.frame(), None) rectInScreenCoords = nsWindow.convertRectToScreen_(rectInWindowCoords) (x, y), (w, h) = rectInScreenCoords y = -(y + h) if getGlyphViewDisplaySettings()['Rulers']: offset = 30 else: offset = 10 return x + offset, y + offset
def applyCallback(self, sender): self._holdGlyphUpdates = True font = CurrentFont() attrValues = self.getAttributes() selection = [] if CurrentGlyphWindow(): selection = [CurrentGlyph().name] else: selection = font.selectedGlyphNames for name in selection: glyph = font[name] glyph.prepareUndo("Shifter") outGlyph = self.getGlyph(glyph, *attrValues) glyph.clear() glyph.appendGlyph(outGlyph) glyph.performUndo() self._holdGlyphUpdates = False
def addSubView(self): self.window = CurrentGlyphWindow() if self.window is None: return self.glyphView.show(True) self.window.addGlyphEditorSubview(self.glyphView) self.glyphView.setSelectedSource()
def updateComp(self, g, viewScale): if len(g.selectedComponents) == 1: cf = g.font cg = g selected_component = cg.selectedComponents[0] selected_component_name = cg.selectedComponents[0].baseGlyph constructions = self.constructions glyph_constructor = self.glyph_constructor glyphWindow = CurrentGlyphWindow() glyphView = glyphWindow.getGlyphView() if not glyphWindow: return for line in glyph_constructor.split("\n"): if len(line) > 0: composed_glyph = line.split("=")[0] recipee = line.split("=")[1] new_base_glyph = recipee.split("+")[0] if new_base_glyph == cg.components[ 0].baseGlyph and cg.name == composed_glyph: construction = f"{composed_glyph}={recipee}" constructionGlyph = GlyphConstructionBuilder( construction, cf) if constructionGlyph.name == cg.name: for component_index, c in enumerate( constructionGlyph.components): c = list(c)[0] if c == selected_component_name: baseGlyphName = constructionGlyph.components[ component_index - 1][0] baseGlyph = cf[baseGlyphName] recipee = construction.split("=")[1] for diacritic_and_anchor in recipee.split( "+")[1:]: diacritic = diacritic_and_anchor.split( "@")[0] anchor = diacritic_and_anchor.split( "@")[1] if diacritic == selected_component_name: selected_component_anchor_name = "_%s" % anchor for baseGlyph_anchor in baseGlyph.anchors: if baseGlyph_anchor.name == anchor: x_baseGlyph_anchor = baseGlyph_anchor.x y_baseGlyph_anchor = baseGlyph_anchor.y selected_comp_baseGlyph = cf[ selected_component_name] for selectedComponent_anchor in selected_comp_baseGlyph.anchors: if selected_component_anchor_name == selectedComponent_anchor.name: x_offset = 0 y_offset = 0 for previous_components in constructionGlyph.components[ 1: component_index]: for cg_component in cg.components: if cg_component.baseGlyph == previous_components[ 0]: x_offset += cg_component.offset[ 0] y_offset += cg_component.offset[ 1] new_x_baseGlyph_anchor = selectedComponent_anchor.x + selected_component.offset[ 0] - x_offset new_y_baseGlyph_anchor = selectedComponent_anchor.y + selected_component.offset[ 1] - y_offset self.drawInfos( new_x_baseGlyph_anchor, new_y_baseGlyph_anchor, viewScale, glyphView, baseGlyph_anchor ) ### Update baseGlyph anchor baseGlyph_anchor.x = new_x_baseGlyph_anchor baseGlyph_anchor.y = new_y_baseGlyph_anchor if self.SettingsWindow.w.updateComposites.get( ) == 1: self.updateRelatedComposites( glyph_constructor, cg, cf, new_base_glyph, baseGlyph_anchor, composed_glyph )
class InterpolationPreviewWindow(object): def __init__(self): self.currentGlyph = None self.window = None self.fonts = [] self.fontNames = [] self.glyph0 = RGlyph() #None self.glyph1 = RGlyph() #None self.compatibilityReport = None self.interpolatedGlyph = RGlyph() self.w = vanilla.FloatingWindow((250, 155), "Interpolation Slider") self.w.open() self.w.title = vanilla.TextBox((10, 10, -10, 25), "Masters:", sizeStyle="small") self.w.font0 = vanilla.PopUpButton((10, 25, -10, 25), [], callback=self.glyphChanged, sizeStyle="small") self.w.font1 = vanilla.PopUpButton((10, 50, -10, 25), [], callback=self.glyphChanged, sizeStyle="small") self.w.compatibilityText = vanilla.TextBox((-105, 83, 100, 35), u"Compatibility: ⚪️", sizeStyle="small") self.w.line = vanilla.HorizontalLine((5, 110, -5, 1)) self.w.interpValue = vanilla.Slider((10, 120, -10, 25), callback=self.optionsChanged, minValue=0, maxValue=1) self.w.interpValue.set(0.5) self.w.bind("close", self.closed) self.collectFonts() self.glyphChanged(None) addObserver(self, "glyphChanged", "currentGlyphChanged") addObserver(self, "fontsChanged", "newFontDidOpen") addObserver(self, "fontsChanged", "fontDidOpen") addObserver(self, "fontsChanged", "fontDidClose") addObserver(self, "drawBkgnd", "drawBackground") addObserver(self, "drawPreview", "drawPreview") def closed(self, sender): if self.window: self.window.getGlyphView().refresh() if self.currentGlyph: self.currentGlyph.removeObserver(self, "Glyph.Changed") self.currentGlyph.removeObserver(self, "Glyph.ContoursChanged") removeObserver(self, "currentGlyphChanged") removeObserver(self, "newFontDidOpen") removeObserver(self, "fontDidOpen") removeObserver(self, "fontDidClose") removeObserver(self, "drawBackground") removeObserver(self, "drawPreview") def getFontName(self, font, fonts): # A helper to get the font name, starting with the preferred name and working back to the PostScript name # Make sure that it's not the same name as another font in the fonts list if font.info.openTypeNamePreferredFamilyName and font.info.openTypeNamePreferredSubfamilyName: name = "%s %s" % (font.info.openTypeNamePreferredFamilyName, font.info.openTypeNamePreferredSubfamilyName) elif font.info.familyName and font.info.styleName: name = "%s %s" % (font.info.familyName, font.info.styleName) elif font.info.fullName: name = font.info.fullName elif font.info.fullName: name = font.info.postscriptFontName else: name = "Untitled" # Add a number to the name if this name already exists if name in fonts: i = 2 while name + " (%s)" % i in fonts: i += 1 name = name + " (%s)" % i return name def collectFonts(self): # Hold aside the current font choices font0idx = self.w.font0.get() font1idx = self.w.font1.get() if not font0idx == -1: font0name = self.fontNames[font0idx] else: font0name = None if not font1idx == -1: font1name = self.fontNames[font1idx] else: font1name = None # Collect info on all open fonts self.fonts = AllFonts() self.fontNames = [] for font in self.fonts: self.fontNames.append(self.getFontName(font, self.fontNames)) # Update the popUpButtons self.w.font0.setItems(self.fontNames) self.w.font1.setItems(self.fontNames) # If there weren't any previous names, try to set the first and second items in the list if font0name == None: if len(self.fonts): self.w.font0.set(0) if font1name == None: if len(self.fonts) >= 1: self.w.font1.set(1) # Otherwise, if there had already been fonts choosen before new fonts were loaded, # try to set the index of the fonts that were already selected if font0name in self.fontNames: self.w.font0.set(self.fontNames.index(font0name)) if font1name in self.fontNames: self.w.font1.set(self.fontNames.index(font1name)) def fontsChanged(self, info): self.collectFonts() self.glyphChanged(None) def glyphChanged(self, info): # Reset the glyph info self.glyph0.clear() self.glyph1.clear() self.compatibilityReport = None self.window = CurrentGlyphWindow() self.interpolatedGlyph.clear() # Remove any observers on the older CurrentGLyph and add them to the new one if self.currentGlyph: self.currentGlyph.removeObserver(self, "Glyph.Changed") self.currentGlyph.removeObserver(self, "Glyph.ContoursChanged") self.currentGlyph = CurrentGlyph() if self.currentGlyph: self.currentGlyph.addObserver(self, "optionsChanged", "Glyph.Changed") self.currentGlyph.addObserver(self, "optionsChanged", "Glyph.ContoursChanged") if not self.currentGlyph == None: # Update the glyph info glyphName = self.currentGlyph.name master0idx = self.w.font0.get() master1idx = self.w.font1.get() master0 = self.fonts[master0idx] master1 = self.fonts[master1idx] if glyphName in master0: self.glyph0.clear() pen = DecomposingPen(master0, self.glyph0.getPen()) master0[glyphName].draw(pen) if glyphName in master1: self.glyph1.clear() pen = DecomposingPen(master1, self.glyph1.getPen()) master1[glyphName].draw(pen) # Update the interp compatibility report self.testCompatibility() # Adjust the frame of the window to fit the interpolation # (Thanks Frederik!) if self.window: widths = [] if self.glyph0: widths.append(self.glyph0.width) if self.glyph1: widths.append(self.glyph1.width) if len(widths): widths.sort() view = self.window.getGlyphView() scale = view.scale() (x, y), (w, h) = view.frame() ox, oy = view.offset() extraWidth = widths[-1] * scale view.setOffset((ox, oy)) view.setFrame_(((x, y), (w + extraWidth, h))) # Update the view self.optionsChanged(None) def testCompatibility(self): status = u"⚪️" if self.window: if self.glyph0 == self.glyph1: status = u"⚪️" elif len(self.interpolatedGlyph.contours) > 0: status = u"✅" else: status = u"❌" self.w.compatibilityText.set(u"Compatibility: %s" % status) def optionsChanged(self, sender): if self.glyph0 and self.glyph1: # Interpolate self.interpolatedGlyph.clear() self.interpolatedGlyph.interpolate(self.w.interpValue.get(), self.glyph0, self.glyph1) self.testCompatibility() # ...and refresh the window if self.window: self.window.getGlyphView().refresh() def addPoints(self, pt0, pt1): return (pt0[0] + pt1[0], pt0[1] + pt1[1]) def subtractPoints(self, pt0, pt1): return (pt0[0] - pt1[0], pt0[1] - pt1[1]) def drawBkgnd(self, info): # Draw the interpolated glyph outlines scale = info["scale"] ptSize = 7 * scale if self.interpolatedGlyph: # Draw the glyph outline pen = CocoaPen(None) self.interpolatedGlyph.draw(pen) dt.fill(r=None, g=None, b=None, a=1) dt.stroke(r=0, g=0, b=0, a=0.4) dt.strokeWidth(2 * scale) dt.save() dt.translate(self.currentGlyph.width) dt.drawPath(pen.path) dt.stroke(r=0, g=0, b=0, a=1) # Draw the points and handles for contour in self.interpolatedGlyph.contours: for bPoint in contour.bPoints: inLoc = self.addPoints(bPoint.anchor, bPoint.bcpIn) outLoc = self.addPoints(bPoint.anchor, bPoint.bcpOut) dt.line(inLoc, bPoint.anchor) dt.line(bPoint.anchor, outLoc) dt.fill(r=1, g=1, b=1, a=1) dt.oval(bPoint.anchor[0] - (ptSize * 0.5), bPoint.anchor[1] - (ptSize * 0.5), ptSize, ptSize) dt.fill(0) # Draw an "X" over each BCP if not bPoint.bcpIn == (0, 0): dt.oval(inLoc[0] - (ptSize * 0.5), inLoc[1] - (ptSize * 0.5), ptSize, ptSize) #dt.line((inLoc[0]-(ptSize*0.5), inLoc[1]-(ptSize*0.5)), (inLoc[0]+(ptSize*0.5), inLoc[1]+(ptSize*0.5))) #dt.line((inLoc[0]+(ptSize*0.5), inLoc[1]-(ptSize*0.5)), (inLoc[0]-(ptSize*0.5), inLoc[1]+(ptSize*0.5))) if not bPoint.bcpOut == (0, 0): dt.oval(outLoc[0] - (ptSize * 0.5), outLoc[1] - (ptSize * 0.5), ptSize, ptSize) #dt.line((outLoc[0]-(ptSize*0.5), outLoc[1]-(ptSize*0.5)), (outLoc[0]+(ptSize*0.5), outLoc[1]+(ptSize*0.5))) #dt.line((outLoc[0]+(ptSize*0.5), outLoc[1]-(ptSize*0.5)), (outLoc[0]-(ptSize*0.5), outLoc[1]+(ptSize*0.5))) dt.restore() def drawPreview(self, info): # Draw a filled in version of the interpolated glyph scale = info["scale"] if self.interpolatedGlyph: pen = CocoaPen(None) self.interpolatedGlyph.draw(pen) dt.fill(r=0, g=0, b=0, a=0.6) dt.stroke(r=None, g=None, b=None, a=1) dt.save() dt.translate(self.currentGlyph.width) dt.drawPath(pen.path) dt.restore()
def wwwindow(self): return CurrentGlyphWindow()
class GlyphMetricsUI(object): def __init__(self): # debug windowname = 'Debug Glyph Metrics UI' windows = [w for w in NSApp().orderedWindows() if w.isVisible()] for window in windows: print(window.title()) if window.title() == windowname: window.close() if debug == True: self.debug = Window((333, 33), windowname) self.debug.bind('close', self.windowSideuiClose) self.debug.open() # setup self.showButtons = False self.sbui = None # add interface addObserver(self, 'addInterface', 'glyphWindowWillOpen') addObserver(self, 'updateSelfWindow', 'currentGlyphChanged') # toggle visibility of tool ui addObserver(self, 'showSideUIonDraw', 'draw') addObserver(self, 'hideSideUIonPreview', 'drawPreview') # remove UI and window manager when glyph window closes # addObserver(self, "observerGlyphWindowWillClose", "glyphWindowWillClose") # subscribe to glyph changes addObserver(self, "viewDidChangeGlyph", "viewDidChangeGlyph") # def observerGlyphWindowWillClose(self, notification): # self.window = notification['window'] # self.cleanup(self.window) # del windowViewManger[notification['window']] # del lll[notification['window']] # del ccc[notification['window']] # del rrr[notification['window']] def windowSideuiClose(self, sender): self.window.removeGlyphEditorSubview(self.sbui) removeObserver(self, 'glyphWindowWillOpen') removeObserver(self, 'glyphWindowWillClose') removeObserver(self, 'glyphWindowWillClose') removeObserver(self, 'currentGlyphChanged') removeObserver(self, 'draw') removeObserver(self, 'drawPreview') removeObserver(self, 'viewDidChangeGlyph') def cleanup(self, w): try: w.removeGlyphEditorSubview(self.sbui) except: return def addInterface(self, notification): self.window = notification['window'] # self.cleanup(self.window) # CONTAINER xywh = (margin, -55, -margin, height) self.sbui = CanvasGroup(xywh, delegate=CanvasStuff(self.window)) # LEFT x, y, w, h = xywh = (0, -height, dfltLwidth, height) this = self.sbui.L = Group(xywh) # text input xywh = (x, y + 3, width * 1.5, height * .75) this.Ltext = EditText(xywh, placeholder='angledLeftMargin', sizeStyle='mini', continuous=False, callback=self.setSB) # quick mod buttons xywh = (x + width * 1.5 + (gap * 1), y, width, height) this.Lminus = Button(xywh, iconminus, sizeStyle='mini', callback=self.setLminus) this.Lminus.getNSButton().setToolTip_('Adjust LSB -' + str(unit)) xywh = (x + width * 2.5 + (gap * 2), y, width, height) this.Lplus = Button(xywh, iconplus, sizeStyle='mini', callback=self.setLplus) this.Lplus.getNSButton().setToolTip_('Adjust LSB +' + str(unit)) xywh = (x + width * 3.5 + (gap * 3), y, width, height) this.Lround = Button(xywh, iconround, sizeStyle='mini', callback=self.setLround) this.Lround.getNSButton().setToolTip_('Round LSB to ' + str(unit)) xywh = (x + width * 4.5 + (gap * 4), y, width, height) this.Lright = Button(xywh, iconcopyR, sizeStyle='mini', callback=self.setLright) this.Lright.getNSButton().setToolTip_('Copy Right Value') # stylize this.Ltext.getNSTextField().setBezeled_(False) this.Ltext.getNSTextField().setBackgroundColor_(NSColor.clearColor()) self.flatButt(this.Lminus) self.flatButt(this.Lplus) self.flatButt(this.Lround) self.flatButt(this.Lright) # RIGHT x, y, w, h = xywh = (-dfltRwidth, y, dfltRwidth, h) this = self.sbui.R = Group(xywh) # text input xywh = (-x - width * 1.5, y + 3, width * 1.5, height * .75) this.Rtext = EditText(xywh, placeholder='angledRightMargin', sizeStyle='mini', continuous=False, callback=self.setSB) # quick mod buttons xywh = (-x - width * 5.5 - (gap * 4), y, width, height) this.Rleft = Button(xywh, iconcopyL, sizeStyle='mini', callback=self.setRleft) this.Rleft.getNSButton().setToolTip_('Copy Left Value') xywh = (-x - width * 4.5 - (gap * 3), y, width, height) this.Rround = Button(xywh, iconround, sizeStyle='mini', callback=self.setRround) this.Rround.getNSButton().setToolTip_('Round RSB to ' + str(unit)) xywh = (-x - width * 3.5 - (gap * 2), y, width, height) this.Rminus = Button(xywh, iconminus, sizeStyle='mini', callback=self.setRminus) this.Rminus.getNSButton().setToolTip_('Adjust RSB -' + str(unit)) xywh = (-x - width * 2.5 - (gap * 1), y, width, height) this.Rplus = Button(xywh, iconplus, sizeStyle='mini', callback=self.setRplus) this.Rplus.getNSButton().setToolTip_('Adjust RSB +' + str(unit)) # stylize this.Rtext.getNSTextField().setBezeled_(False) this.Rtext.getNSTextField().setBackgroundColor_(NSColor.clearColor()) this.Rtext.getNSTextField().setAlignment_(NSTextAlignmentRight) self.flatButt(this.Rminus) self.flatButt(this.Rplus) self.flatButt(this.Rround) self.flatButt(this.Rleft) # CENTER winX, winY, winW, winH = self.window.getVisibleRect() winW = winW - margin * 5 x, y, w, h = xywh = ((winW / 2) - (dfltCwidth / 2), y, dfltCwidth, h) this = self.sbui.C = Group(xywh) x = 0 # text input c = (dfltCwidth / 2) xywh = (c - (width * .75), y + 3, width * 1.5, height * .75) this.Ctext = EditText(xywh, placeholder='width', sizeStyle='mini', continuous=False, callback=self.setSB) # quick mod buttons xywh = (c - (width * .75) - width * 2 - (gap * 2), y, width, height) this.Ccenter = Button(xywh, iconcenter, sizeStyle='mini', callback=self.setCcenter) this.Ccenter.getNSButton().setToolTip_('Center on Width') xywh = (c - (width * .75) - width - (gap * 1), y, width, height) this.Cround = Button(xywh, iconround, sizeStyle='mini', callback=self.setCround) this.Cround.getNSButton().setToolTip_('Round Width to ' + str(unit)) xywh = (c + (width * .75) + (gap * 1), y, width, height) this.Cminus = Button(xywh, iconminus, sizeStyle='mini', callback=self.setCminus) this.Cminus.getNSButton().setToolTip_('Adjust Width -' + str(2 * unit)) xywh = (c + (width * .75) + width + (gap * 2), y, width, height) this.Cplus = Button(xywh, iconplus, sizeStyle='mini', callback=self.setCplus) this.Cplus.getNSButton().setToolTip_('Adjust Width +' + str(2 * unit)) # stylize this.Ctext.getNSTextField().setBezeled_(False) this.Ctext.getNSTextField().setBackgroundColor_(NSColor.clearColor()) this.Ctext.getNSTextField().setAlignment_(NSTextAlignmentCenter) self.flatButt(this.Cminus) self.flatButt(this.Cplus) self.flatButt(this.Cround) self.flatButt(this.Ccenter) # hide self.sbui.L.Lminus.show(False) self.sbui.L.Lround.show(False) self.sbui.L.Lplus.show(False) self.sbui.L.Lright.show(False) self.sbui.R.Rminus.show(False) self.sbui.R.Rround.show(False) self.sbui.R.Rplus.show(False) self.sbui.R.Rleft.show(False) self.sbui.C.Cminus.show(False) self.sbui.C.Cround.show(False) self.sbui.C.Ccenter.show(False) self.sbui.C.Cplus.show(False) # make it real self.sbWatcherInitialize() self.window.addGlyphEditorSubview(self.sbui) self.updateValues() self.buildMatchBase() windowViewManger[self.window] = self.sbui def updateSelfWindow(self, notification): self.window = CurrentGlyphWindow() self.buildMatchBase() self.updateValues() def showSideUIonDraw(self, notification): self.sbWatcher() sbui = windowViewManger.get(self.window) if sbui is not None: sbui.show(True) def hideSideUIonPreview(self, notification): sbui = windowViewManger.get(self.window) if sbui is not None: sbui.show(False) def updateValues(self, notification=None): try: g = self.window.getGlyph() f = g.font sbui = windowViewManger.get(self.window) sbui.L.Ltext.set(str(g.angledLeftMargin)) sbui.R.Rtext.set(str(g.angledRightMargin)) sbui.C.Ctext.set(str(g.width)) except Exception as e: # if debug == True: # print('Exception updateValues', e) return # hack sidebearings changed observer # used when things redraw def sbWatcherInitialize(self): g = self.window.getGlyph() f = g.font if g is not None: lll[self.window] = g.angledLeftMargin ccc[self.window] = g.width rrr[self.window] = g.angledRightMargin def sbWatcher(self): g = CurrentGlyph() if g is not None: f = g.font if lll[self.window] != None and ccc[self.window] != None and rrr[ self.window] != None: if lll[self.window] != g.angledLeftMargin or ccc[ self.window] != g.width or rrr[ self.window] != g.angledRightMargin: lll[self.window] = g.angledLeftMargin ccc[self.window] = g.width rrr[self.window] = g.angledRightMargin self.updateValues() self.buildMatchBase() def setSB(self, sender): changeAttribute = sender.getPlaceholder() g = self.window.getGlyph() f = g.font v = sender.get() if is_number(v): if debug == True: print('value is a number') g.prepareUndo('Change ' + changeAttribute + ' SB') setattr(g, changeAttribute, float(v)) g.performUndo() self.updateValues() elif v in f: if debug == True: print('value is a glyph') g.prepareUndo('Change ' + changeAttribute + ' SB') sb = getattr(f[v], changeAttribute) setattr(g, changeAttribute, sb) g.performUndo() self.updateValues() else: if debug == True: print('value is not a number or a glyph') return def setLminus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➖ Left SB') g.angledLeftMargin += -1 * unit g.performUndo() self.updateValues() def setLround(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Round Left SB') g.angledLeftMargin = int(unit * round(float(g.angledLeftMargin) / unit)) g.performUndo() self.updateValues() def setLplus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➕ Left SB') g.angledLeftMargin += unit g.performUndo() self.updateValues() def setLright(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Copy Right SB') g.angledLeftMargin = g.angledRightMargin g.performUndo() self.updateValues() def setLmatch(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Match Left SB') f = g.font gmatch = sender.getTitle() if f[gmatch] is not None: g.angledLeftMargin = f[gmatch].angledLeftMargin g.performUndo() self.updateValues() def setRminus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➖ Right SB') g.angledRightMargin += -1 * unit g.performUndo() self.updateValues() def setRround(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Round Right SB') g.angledRightMargin = int(unit * round(float(g.angledRightMargin) / unit)) g.performUndo() self.updateValues() def setRplus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➕ Right SB') g.angledRightMargin += unit g.performUndo() self.updateValues() def setRmatch(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Match Right SB') gmatch = sender.getTitle() f = g.font if f[gmatch] is not None: g.angledRightMargin = f[gmatch].angledRightMargin g.performUndo() self.updateValues() def setRleft(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Copy Left SB') g.angledRightMargin = g.angledLeftMargin g.performUndo() self.updateValues() def setCminus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➖ Width') # use whole units, not floats oldwidth = g.width leftby = unit g.angledLeftMargin += -1 * leftby g.width = oldwidth - unit * 2 g.performUndo() self.updateValues() def setCround(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Round Width') g.width = int(unit * round(float(g.width) / unit)) g.performUndo() self.updateValues() def setCcenter(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Center on Width') # use whole units, not floats padding = g.angledLeftMargin + g.angledRightMargin gwidth = g.width g.angledLeftMargin = int(padding / 2) g.width = gwidth g.performUndo() self.updateValues() def setCplus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➕ Width') # use whole units, not floats oldwidth = g.width leftby = unit g.angledLeftMargin += leftby g.width = oldwidth + unit * 2 g.performUndo() self.updateValues() def setCmatch(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Match Width') f = g.font gmatch = sender.getTitle() if f[gmatch] is not None: g.width = f[gmatch].width g.performUndo() self.updateValues() def flatButt(self, this, match=False): this = this.getNSButton() this.setBezelStyle_(buttstyle) this.setBordered_(False) this.setWantsLayer_(True) this.setBackgroundColor_(NSColor.whiteColor()) if match == True: this.setBackgroundColor_( NSColor.colorWithCalibratedRed_green_blue_alpha_( .9, 1, .85, 1)) def buildMatchBase(self, notification=None): self.newheight = height try: g = self.window.getGlyph() f = g.font # remove old buttons for i in range(10): if hasattr(self.sbui.L, 'buttobj_%s' % i): delattr(self.sbui.L, 'buttobj_%s' % i) delattr(self.sbui.R, 'buttobj_%s' % i) delattr(self.sbui.C, 'buttobj_%s' % i) # add button for each component self.uniquecomponents = [] for c in g.components: if c.baseGlyph not in self.uniquecomponents: self.uniquecomponents.append(c.baseGlyph) for i, c in enumerate(self.uniquecomponents): row = i + 1 yy = -height * (row + 1) - 3 xywh = (0, yy, width * 5.5 + (gap * 4), height) buttobj = Button(xywh, c, sizeStyle='mini', callback=self.setLmatch) setattr(self.sbui.L, 'buttobj_%s' % i, buttobj) this = getattr(self.sbui.L, 'buttobj_%s' % i) this.getNSButton().setAlignment_(NSTextAlignmentLeft) this.getNSButton().setToolTip_('Match LSB of ' + c) xywh = (-width * 5.5 - (gap * 4), yy, width * 5.5 + (gap * 4), height) buttobj = Button(xywh, c, sizeStyle='mini', callback=self.setRmatch) setattr(self.sbui.R, 'buttobj_%s' % i, buttobj) this = getattr(self.sbui.R, 'buttobj_%s' % i) this.getNSButton().setAlignment_(NSTextAlignmentRight) this.getNSButton().setToolTip_('Match RSB of ' + c) xywh = ((dfltLwidth / 2) - (width * 2.75 + (gap * 2)), yy, width * 5.5 + (gap * 4), height) buttobj = Button(xywh, c, sizeStyle='mini', callback=self.setCmatch) setattr(self.sbui.C, 'buttobj_%s' % i, buttobj) this = getattr(self.sbui.C, 'buttobj_%s' % i) this.getNSButton().setToolTip_('Match Width of ' + c) for i, c in enumerate(self.uniquecomponents): try: this = getattr(self.sbui.L, 'buttobj_%s' % i) # hide if hidden if self.showButtons == False: this.show(False) # check if metrics match base glyphs if int(f[c].angledLeftMargin) == int(g.angledLeftMargin): self.flatButt(this, True) else: self.flatButt(this) this = getattr(self.sbui.R, 'buttobj_%s' % i) if self.showButtons == False: this.show(False) if int(f[c].angledRightMargin) == int(g.angledRightMargin): self.flatButt(this, True) else: self.flatButt(this) this = getattr(self.sbui.C, 'buttobj_%s' % i) if self.showButtons == False: this.show(False) if f[c].width == g.width: self.flatButt(this, True) else: self.flatButt(this) except Exception as e: return # change height of canvas to fit buttons self.newheight = height * (len(self.uniquecomponents) + 2) newy = -55 - height * (len(self.uniquecomponents)) self.sbui.setPosSize((margin, newy, -margin, self.newheight)) self.sbui.L.setPosSize((0, 0, dfltLwidth, self.newheight)) self.sbui.R.setPosSize( (-dfltRwidth, 0, dfltRwidth, self.newheight)) winX, winY, winW, winH = self.window.getVisibleRect() winW = winW - margin * 5 offsetcenter = (winW / 2) - (width * 2.25) self.sbui.C.setPosSize( (offsetcenter, 0, width * 5 + 12, self.newheight)) except Exception as e: return ############################################# # watch for glyph changes ############################################# def viewDidChangeGlyph(self, notification): self.glyph = CurrentGlyph() self.unsubscribeGlyph() self.subscribeGlyph() self.glyphChanged() def subscribeGlyph(self): self.glyph.addObserver(self, 'glyphChanged', 'Glyph.Changed') def unsubscribeGlyph(self): if self.glyph is None: return self.glyph.removeObserver(self, 'Glyph.Changed') def glyphChanged(self, notification=None): self.updateValues()
def updateSelfWindow(self, notification): self.window = CurrentGlyphWindow() self.buildMatchBase() self.updateValues()
def gotoGlyph(self, sender=None, glyph=None): newGlyphName = sender.get()[sender.getSelection()[0]]["Name"] #print("Goto Glyph:", newGlyphName) CurrentGlyphWindow().setGlyphByName(newGlyphName)
self = super(PasteGlyphComboBoxDataSource, self).init() self._glyphNames = [] return self def setGlyphNames_(self, names): self._glyphNames = names def comboBox_completedString_(self, comboBox, text): if text in self._glyphNames: return text for name in self._glyphNames: if name.startswith(text): return name return text def comboBox_indexOfItemWithStringValue_(self, comboBox, text): if text not in self._glyphNames: return -1 return self._glyphNames.index(text) def comboBox_objectValueForItemAtIndex_(self, comboBox, index): return self._glyphNames[index] def numberOfItemsInComboBox_(self, comboBox): return len(self._glyphNames) if __name__ == "__main__": from mojo.UI import CurrentGlyphWindow glyphWindow = CurrentGlyphWindow() PasteGlyphWindowController(glyphWindow)
def __init__(self, glyph, construction, decompose): self.glyph = glyph width = 350 height = 120 editorWindow = CurrentGlyphWindow() (editorX, editorY, editorW, editorH), screen = getGlyphEditorRectAndScreen(editorWindow) x = editorX + ((editorW - width) / 2) y = editorY + ((editorH - height) / 2) self.w = StatusInteractivePopUpWindow((x, y, width, height), screen=screen) self.w.constructionEditor = vanilla.EditText( "auto", construction ) self.w.decomposeCheckBox = vanilla.CheckBox( "auto", "Decompose", value=decompose ) self.w.open() self.w.line = vanilla.HorizontalLine("auto") self.w.flex = vanilla.Group("auto") self.w.cancelButton = vanilla.Button( "auto", "Cancel", callback=self.cancelButtonCallback ) self.w.buildButton = vanilla.Button( "auto", "Build", callback=self.buildButtonCallback ) metrics = dict( margin=15, padding=10, buttonWidth=80 ) rules = [ "H:|-margin-[constructionEditor]-margin-|", "H:|-margin-[line]-margin-|", "H:|-margin-[decomposeCheckBox][flex(>=100)]-[cancelButton(==buttonWidth)]-padding-[buildButton(==buttonWidth)]-margin-|", "V:|" "-margin-" "[constructionEditor]" "-padding-" "[line]" "-padding-" "[decomposeCheckBox]" "-margin-" "|", "V:|" "-margin-" "[constructionEditor]" "-padding-" "[line]" "-padding-" "[flex]" "-margin-" "|", "V:|" "-margin-" "[constructionEditor]" "-padding-" "[line]" "-padding-" "[cancelButton]" "-margin-" "|", "V:|" "-margin-" "[constructionEditor]" "-padding-" "[line]" "-padding-" "[buildButton]" "-margin-" "|" ] self.w.addAutoPosSizeRules(rules, metrics) self.w.setDefaultButton(self.w.buildButton) self.w.cancelButton.bind(".", ["command"]) self.w.getNSWindow().makeFirstResponder_(self.w.constructionEditor.getNSTextField()) self.w.open()