def show(self, screen, callback): if callback: assert callable(callback) if len(self.buttons) == 0: self.buttons.append('Dismiss') numButtons = len(self.buttons) totalButtonsWidth = (self.minimumButtonWidth + self.buttonPadding ) * numButtons + self.buttonPadding width = max(totalButtonsWidth, self.minimumViewWidth) buttonWidth = floor((width - self.buttonPadding) / numButtons - self.buttonPadding) alertView = titled.TitledView() alertView.caption = self.title alertView.frame = alertView.frameForContentFrame( core.Rect(0, 0, width, self.viewHeight)) bounds = alertView.contentBounds() rect = core.Rect(bounds) rect.y += self.buttonHeight + self.buttonPadding rect.height -= self.buttonHeight + self.buttonPadding textView = label.Label(self.text, frame=rect) textView.fontAttributes = font.attributes(18) textView.align = label.ALIGN_CENTER textView.textColor = core.Color(0, 0, 0) textView.backgroundColor = core.Color(1, 1, 1) rect.origin = bounds.x, bounds.y rect.size = buttonWidth, self.buttonHeight rect.x += self.buttonPadding rect.y += self.buttonPadding print('bounds', bounds) print('rect', rect) tag = 0 for txt in self.buttons: btn = button.Button(txt, frame=rect) btn.tag = tag tag += 1 btn.addTarget(self, self._onButtonClick) alertView.addChild(btn) rect.x += self.buttonPadding + buttonWidth alertView.addChild(textView) self._modal = modal.Modal(alertView) self._modal.present(screen.frame, self._onModalCallback) self.callback = callback
def __init__(self, type=TYPE_DIRECTIONAL, pos=core.Vector3(0, 1, 0), color=core.Color(1, 1, 1, 1)): core.Light.__init__(self, type, pos, color) self.constAttenuation = 1 self.linearAttenuation = 0 self.quadraticAttenuation = 0
def __init__(self, renderer, texture, sampler=None, color=core.Color(1,1,1,1), blend=blendstate.defaultOpaque): self.vertices = array.array('f') self.renderer = renderer self.color = color self.blend = blend self.texture = texture self.sampler = sampler self.count = 0
def RenderTextBaseline(renderer, font, begin, align, color=core.Color(0, 0, 0), blend=blendstate.defaultAlpha): cls = RenderTextClass(align) return cls(renderer, font, begin, color, blend)
def render(self, renderer): rc = renderer.contextForTexturedRects(texture=None, blend=blendstate.defaultAlpha) self._render(rc, core.Matrix3(), core.Color(1, 1, 1, 1)) if rc.texture: # draw rectangles with rc: pass
class ItemStyle: indentationLevelWidth = 15 expandIconSize = 12 padding = 2 topMargin = 1 leftMargin = 4 rightMargin = 1 bottomMargin = 1 fontAttributes = font.attributes(14, embolden=0) backgroundColor = None backgroundColorHighlighted = core.Color(1, 1, 0) backgroundColorActivated = core.Color(0.25, 0.25, 1.0) backgroundColorDisabled = core.Color(0.9, 0.9, 0.9) textColor = core.Color(0, 0, 0) textColorHighlighted = core.Color(0, 0, 0) textColorActivated = core.Color(1, 1, 1) textColorDisabled = core.Color(0.5, 0.5, 0.5) outlineColor = None outlineColorHighlighted = None outlineColorActivated = core.Color(0, 0, 0, 1) outlineColorDisabled = None backgroundColorBlend = blendstate.defaultOpaque arrowColor = core.Color(0.5, 0.5, 0.5) arrowColorActivated = core.Color(0.25, 0.25, 1.0) iconTransform = core.Matrix3() scaleFactor = view.DEFAULT_UI_SCALE def __init__(self): self.font = None
def create(cls, **kwargs): bs = cls() bs.srcBlendRGB = kwargs.get('srcBlendRGB', BLEND_MODE_ONE) bs.srcBlendAlpha = kwargs.get('srcBlendAlpha', BLEND_MODE_ONE) bs.dstBlendRGB = kwargs.get('dstBlendRGB', BLEND_MODE_ZERO) bs.dstBlendAlpha = kwargs.get('dstBlendAlpha', BLEND_MODE_ZERO) bs.blendFuncRGB = kwargs.get('blendFuncRGB', BLEND_FUNC_ADD) bs.blendFuncAlpha = kwargs.get('blendFuncAlpha', BLEND_FUNC_ADD) bs.colorWriteR = kwargs.get('colorWriteR', True) bs.colorWriteG = kwargs.get('colorWriteG', True) bs.colorWriteB = kwargs.get('colorWriteB', True) bs.colorWriteA = kwargs.get('colorWriteA', True) bs.constantColor = kwargs.get('constantColor', core.Color(0.0, 0.0, 0.0, 0.0)) return bs
class Label(view.View): fontAttributes = font.attributes(12) textColor = core.Color(0.0, 0.0, 0.0) outlineColor = None textTransform = core.Matrix3() scaleToFit = False leftMargin = 0 topMargin = 0 rightMargin = 0 bottomMargin = 0 def __init__(self, text='', *args, **kwargs): super().__init__(*args, **kwargs) self.text = str(text) self.align = ALIGN_CENTER self.linebreak = LINE_BREAK_TRUNCATING_TAIL def onLoaded(self): super().onLoaded() if not self.font: self.font = font.loadUIFont(self.fontAttributes, self.scaleFactor) def onUnload(self): self.font = None super().onUnload() def onRender(self, renderer): super().onRender(renderer) if self.font: bounds = self.contentBounds() bounds.x += self.leftMargin bounds.y += self.bottomMargin bounds.width -= self.leftMargin + self.rightMargin bounds.height -= self.topMargin + self.bottomMargin font.drawText(renderer, bounds, self.text, self.font, self.textColor, self.outlineColor, scaleToFit=self.scaleToFit, align=self.align, linebreak=self.linebreak, blend=blendstate.defaultAlpha)
def __init__(self, renderer, font, begin, color=core.Color(0, 0, 0), blend=blendstate.defaultAlpha): self.renderer = renderer self.font = font self.color = color self.blend = blend self.lines = [] self.line_begin = begin.x self.scaleFactor = _pixelScaleFactor(renderer) self.line_height = font.lineHeight() * self.scaleFactor[1] self.position = core.Point(begin)
class ItemStyle: """ item layout with icon | left-margin | checkbox | padding | icon | padding | text | text-margin | arrow or shortcut | right-margin | item layout without icon | left-margin | checkbox | padding | text | padding | arrow or shortcut | right-margin | separator horizontal/vertical layout | separator-margin | separator-line | separator-margin | """ minimumHeight = 12 minimumWidth = 48 topMargin = 4 bottomMargin = 4 leftMargin = 2 rightMargin = 4 textMargin = 12 checkmarkSize = 12 imageSize = 12 arrowSize = 9 padding = 2 separatorColor = core.Color(0.75, 0.75, 0.75) separatorMargin = 4 separatorWidth = 1 textColor = core.Color(0, 0, 0) textColorHighlighted = core.Color(1, 1, 1) textColorDisabled = core.Color(0.56, 0.56, 0.56) outlineColor = None outlineColorHighlighted = None outlineColorDisabled = None checkmarkColor = textColor checkmarkColorHighlighted = textColorHighlighted checkmarkColorDisabled = textColorDisabled arrowColor = core.Color(0.26, 0.26, 0.26) arrowColorHighlighted = core.Color(0.94, 0.94, 0.94) arrowColorDisabled = textColorDisabled def __init__(self): self.font = None
def _render(self, rc, transform, color): if self.hidden: return cc = core.Color(self.diffuse[0] * color.r, self.diffuse[1] * color.g, self.diffuse[2] * color.b, self.alpha[0] * color.a) if cc.a > 0.0: transform = self.transform * transform # get texture ids if self.texturePack: texIds = self._textureIdsForState[self.state] if len(texIds): texIndex = round(self.textureIndex[0]) % len(texIds) texKey = texIds[texIndex] frame = self.texturePack.frames.get(texKey) else: frame = self.texturePack.frames.get(self.name) if frame: # draw other Sprites if rc.texture != self.texturePack.texture or rc.color.argb32Value( ) != cc.argb32Value(): if rc.texture: with rc: pass rc.texture = self.texturePack.texture rc.color = cc # calculate texture frame. rect = core.Rect(self.size[0] * frame.offset.x, self.size[1] * frame.offset.y, self.size[0] * frame.scale.width, self.size[1] * frame.scale.height) rc.add(rect, transform, self.TEXTURE_RECT, frame.transform) for c in self.children: c._render(rc, transform, cc)
def add(self, shape, transform, faceColor, edgeColor=core.Color(0.0, 0.0, 0.0)): co = core.CollisionObject() co.setCollisionShape(shape) co.setWorldTransform(transform) self.objects.append(co) self.colorTable[co] = (faceColor, edgeColor)
class TextField(textinput.TextInput, control.Control, view.View): borderWidth = 1 caretBlinkInterval = 0.5 caretWidth = 1 caretColor = core.Color(0.0, 0.0, 0.0) caretColorComposition = core.Color(0.0, 0.0, 0.75) selectionColor = core.Color(0.5, 0.5, 1.0) selectionColorInactivated = core.Color(0.6, 0.6, 1.0) selectionColorDisabled = core.Color(1.0, 0.0, 0.0) textColor = core.Color(0.0, 0.0, 0.0) textColorInactivated = core.Color(0.3, 0.3, 0.3) textColorDisabled = core.Color(0.4, 0.4, 0.4) textColorSelected = core.Color(1.0, 1.0, 1.0) textColorSelectedInactivated = core.Color(0.9, 0.9, 0.9) textColorSelectedDisabled = core.Color(0.4, 0.4, 1.0) textColorComposition = core.Color(0.0, 0.0, 0.5) textColorCompositionUnderCaret = core.Color(1.0, 1.0, 1.0) outlineColor = None outlineColorInactivated = None outlineColorDisabled = None outlineColorSelected = None outlineColorSelectedInactivated = None outlineColorSelectedDisabled = None outlineColorComposition = None outlineColorCompositionUnderCaret = None backgroundColor = core.Color(1.0, 1.0, 1.0) backgroundColorDisabled = core.Color(0.6, 0.6, 0.6) fontAttributes = font.attributes(14, kerning=False, file='SeoulNamsanM.ttf') keyboardId = 0 acceptTab = False tabSpace = 4 keyPressingDelay = 0.3 keyRepeatInterval = 0.04 def __init__(self, text='', *args, **kwargs): super().__init__(*args, **kwargs) self.__text = text self.__caretPos = 0 self.__selectionBegin = -1 # selection: from __selectionBegin to __caretPos self.__timer = core.Timer() self.__editing = False self.__caretVisible = False self.__capturedMouseId = None self.__unmodifiedText = '' @property def text(self): return self.__text @text.setter def text(self, value): self.__text = value self.caretPos = self.__caretPos @property def caretPos(self): return self.__caretPos @caretPos.setter def caretPos(self, value): value = max(int(value), 0) if self.__caretPos != value: print('setCaretPos:', value) self.__caretPos = value tl = len(self.__text) if self.__caretPos > tl: self.__caretPos = tl self.__selectionBegin = -1 if self.enabled: self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() @property def editing(self): return self.__editing @editing.setter def editing(self, value): b = bool(value) if self.__editing != b: self.__editing = b if self.__editing: self.captureKeyboard(self.keyboardId) self.screen().window.setTextInputEnabled(self.keyboardId, True) self.__caretVisible = True self.__unmodifiedText = self.__text print('textfield:{} capture keyboard:{}'.format( id(self), self.keyboardId)) else: self.screen().window.setTextInputEnabled( self.keyboardId, False) self.releaseKeyboard(self.keyboardId) self.__caretVisible = False self.__unmodifiedText = '' print('textfield:{} release keyboard:{}'.format( id(self), self.keyboardId)) self.__timer.reset() self.redraw() def selectionRange(self): if self.__selectionBegin >= 0 and self.__selectionBegin != self.__caretPos: if self.__selectionBegin > self.__caretPos: return self.__caretPos, self.__selectionBegin else: return self.__selectionBegin, self.__caretPos return self.__caretPos, self.__caretPos def onLoaded(self): super().onLoaded() self.minimumViewHeight = self.font.lineHeight() / self.scaleFactor self.updateScroll() def onUnload(self): super().onUnload() def onResized(self): super().onResized() self.updateScroll() def onUpdate(self, delta, tick, date): super().onUpdate(delta, tick, date) if self.__editing and self.enabled: if self.__timer.elapsed() > self.caretBlinkInterval: self.__caretVisible = not self.__caretVisible self.__timer.reset() self.redraw() def characterIndexAtPosition(self, pos): bounds = self.contentBounds() x = pos.x - bounds.x invScale = 1.0 / self.scaleFactor lineWidth = lambda s: self.font.lineWidth(s) * invScale if x <= 0: return 0 if x >= lineWidth(self.text): return len(self.text) width = 0 width2 = 0 index = 0 while width < x: width2 = width index += 1 width = lineWidth(self.text[0:index]) if abs(x - width2) < abs(x - width): return index - 1 return index def updateScroll(self): bounds = self.contentBounds() if self.font: invScale = 1.0 / self.scaleFactor charWidth = self.font.width * invScale if bounds.width > charWidth * 2: # at least 2 characters should be displayed. maxX = bounds.width * 0.9 minX = bounds.width * 0.1 text = self.__text + self.composingText textLen = self.font.lineWidth(text) * invScale if textLen > maxX: text = self.__text[0:self.__caretPos] + self.composingText textLen = self.font.lineWidth(text) * invScale transform = core.AffineTransform2( core.Matrix3(self.contentTransform)) textLen += transform.translation[0] indent = min(bounds.width * 0.1, charWidth) offset = 0 while textLen < minX: textLen += indent offset += indent while textLen > maxX: textLen -= indent offset -= indent if offset != 0: transform.translate(core.Vector2(offset, 0)) pos = transform.translation if pos[0] > 0: transform.translation = 0, pos[1] self.contentTransform = transform.matrix3() return self.contentTransform = core.Matrix3() def onRender(self, renderer): invScale = 1.0 / self.scaleFactor bounds = self.contentBounds() height = self.font.lineHeight() * invScale offsetX = bounds.x offsetY = int(bounds.y + (bounds.height - height) * 0.5) lineWidth = lambda text: self.font.lineWidth(text) * invScale drawText = lambda rect, text, tc, oc: \ font.drawText(renderer, rect, text, self.font, tc, oc, align=font.ALIGN_BOTTOM_LEFT, linebreak=font.LINE_BREAK_CLIPPING) if self.enabled: super().onRender(renderer) if self.isKeyboardCapturedBySelf(self.keyboardId): # activated textColor = self.textColor outlineColor = self.outlineColor textColorSelected = self.textColorSelected outlineColorSelected = self.outlineColorSelected selectionColor = self.selectionColor else: textColor = self.textColorInactivated outlineColor = self.outlineColorInactivated textColorSelected = self.textColorSelectedInactivated outlineColorSelected = self.outlineColorSelectedInactivated selectionColor = self.selectionColorInactivated else: # disabled tmp = self.backgroundColor self.backgroundColor = self.backgroundColorDisabled super().onRender(renderer) self.backgroundColor = tmp self.__caretVisible = False textColor = self.textColorDisabled outlineColor = self.outlineColorDisabled textColorSelected = self.textColorSelectedDisabled outlineColorSelected = self.outlineColorSelectedDisabled selectionColor = self.selectionColorDisabled selectionRange = self.selectionRange() if selectionRange[0] != selectionRange[1]: left = self.__text[0:selectionRange[0]] right = self.__text[selectionRange[1]:] else: left = self.__text[0:self.__caretPos] right = self.__text[self.__caretPos:] width = lineWidth(left) rc = core.Rect(offsetX, offsetY, width, height) drawText(rc, left, textColor, outlineColor) offsetX += width if selectionRange[0] != selectionRange[1]: text = self.__text[selectionRange[0]:selectionRange[1]] width = lineWidth(text) rc = core.Rect(offsetX, offsetY, width, height) with renderer.contextForSolidRects(selectionColor) as r: r.add(rc) drawText(rc, text, textColorSelected, outlineColorSelected) if self.__caretVisible: if self.__caretPos > self.__selectionBegin: rc = core.Rect(offsetX + width, offsetY, self.caretWidth, height) else: rc = core.Rect(offsetX, offsetY, self.caretWidth, height) with renderer.contextForSolidRects(self.caretColor) as r: r.add(rc) offsetX += width else: if len(self.composingText) > 0: width = lineWidth(self.composingText) rc = core.Rect(offsetX, offsetY, width, height) if self.__caretVisible: with renderer.contextForSolidRects( self.caretColorComposition) as r: r.add(rc) drawText(rc, self.composingText, self.textColorCompositionUnderCaret, self.outlineColorCompositionUnderCaret) else: drawText(rc, self.composingText, self.textColorComposition, self.outlineColorComposition) offsetX += width else: if self.__caretVisible: rc = core.Rect(offsetX, offsetY, self.caretWidth, height) with renderer.contextForSolidRects(self.caretColor) as r: r.add(rc) width = lineWidth(right) rc = core.Rect(offsetX, offsetY, width, height) drawText(rc, right, textColor, outlineColor) def onMouseDown(self, deviceId, buttonId, pos): editing = self.__editing selectionRange = self.selectionRange() self.editing = True if self.__capturedMouseId is None and buttonId == 0: if editing or selectionRange[0] == selectionRange[1]: self.captureMouse(deviceId) index = self.characterIndexAtPosition(pos) self.__capturedMouseId = deviceId self.caretPos = index self.redraw() return super().onMouseDown(deviceId, buttonId, pos) def onMouseUp(self, deviceId, buttonId, pos): if buttonId == 0 and self.__capturedMouseId == deviceId: self.__capturedMouseId = None if self.__selectionBegin == self.__caretPos: self.__selectionBegin = -1 self.releaseMouse(deviceId) return super().onMouseUp(deviceId, buttonId, pos) def onMouseMove(self, deviceId, pos, delta): if self.__capturedMouseId == deviceId: index = self.characterIndexAtPosition(pos) if self.__selectionBegin < 0: self.__selectionBegin = self.__caretPos self.__caretPos = index self.updateScroll() self.redraw() return super().onMouseMove(deviceId, pos, delta) def onMouseLost(self, deviceId): self.__capturedMouseId = None return super().onMouseLost(deviceId) def onKeyboardLost(self, deviceId): print('textfield:{}.onKeyboardLost:{}'.format(id(self), deviceId)) self.editing = False return super().onKeyboardLost(deviceId) def moveLeft(self): if self.shiftDown: if self.__selectionBegin < 0: self.__selectionBegin = self.__caretPos self.__caretPos = max(self.__caretPos - 1, 0) else: range = self.selectionRange() if range[0] == range[1]: self.__caretPos = max(self.__caretPos - 1, 0) else: self.__caretPos = range[0] self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def moveToBeginningOfLine(self): if self.shiftDown: if self.__selectionBegin < 0: self.__selectionBegin = self.__caretPos self.__caretPos = 0 else: self.__caretPos = 0 self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def moveRight(self): if self.shiftDown: if self.__selectionBegin < 0: self.__selectionBegin = self.__caretPos self.__caretPos = min(len(self.__text), self.__caretPos + 1) else: range = self.selectionRange() if range[0] == range[1]: self.__caretPos = min(len(self.__text), self.__caretPos + 1) else: self.__caretPos = range[1] self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def moveToEndOfLine(self): super().moveToEndOfLine() if self.shiftDown: if self.__selectionBegin < 0: self.__selectionBegin = self.__caretPos self.__caretPos = len(self.__text) else: self.__caretPos = len(self.__text) self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def insertText(self, text): range = self.selectionRange() left = self.__text[0:range[0]] right = self.__text[range[1]:] if range[0] == range[1]: self.__text = left + text + right self.__caretPos += len(text) else: self.__text = left + text self.__caretPos = len(self.text) self.text += right self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def setCompositionText(self, text): range = self.selectionRange() if range[0] != range[1]: left = self.__text[0:range[0]] right = self.__text[range[1]:] self.__text = left + right self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def deleteBackward(self): range = self.selectionRange() if range[0] == range[1]: if range[0] > 0: left = self.__text[0:range[0] - 1] right = self.__text[range[1]:] self.__text = left + right self.__caretPos -= 1 else: left = self.__text[0:range[0]] right = self.__text[range[1]:] self.__text = left + right self.__caretPos = range[0] self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def deleteForward(self): range = self.selectionRange() if range[0] == range[1]: if range[1] < len(self.__text): left = self.__text[0:range[0]] right = self.__text[range[1] + 1:] self.__text = left + right else: left = self.__text[0:range[0]] right = self.__text[range[1]:] self.__text = left + right self.__caretPos = range[0] self.__selectionBegin = -1 self.__caretVisible = True self.__timer.reset() self.updateScroll() self.redraw() def processEscape(self): if self.editing: self.__text = self.__unmodifiedText self.__caretPos = 0 self.__selectionBegin = -1 self.editing = False self.updateScroll() # post notification print('User cancelled editing. (post notification)') def processCarriageReturn(self): if self.editing: self.__caretPos = 0 self.__selectionBegin = -1 self.editing = False self.updateScroll() # post notification! print('User finish editing. (post notification)') def processLineFeed(self): self.processCarriageReturn() def insertTab(self): self.processCarriageReturn()
class RadioButton(control.Control, view.View): radius = 8 circleBorder = 1 innerCircleRadius = 5 leftMargin = 4 rightMargin = 0 padding = 8 circleBorderColor = core.Color(0.4, 0.4, 0.4) circleColor = core.Color(0.9, 0.9, 1.0) circleColorHighlighted = core.Color(0.8, 0.8, 1.0) circleColorActivated = core.Color(0.6, 0.6, 1.0) circleColorDisabled = core.Color(0.6, 0.6, 0.6) innerCircleColor = core.Color(0.25, 0.25, 0.25) innerCircleColorHighlighted = core.Color(0.25, 0.25, 1.0) innerCircleColorActivated = core.Color(0.0, 0.0, 0.6) innerCircleColorDisabled = core.Color(0.2, 0.2, 0.2) textColor = core.Color(0.0, 0.0, 0.0) textColorHighlighted = core.Color(0.0, 0.0, 0.0) textColorActivated = core.Color(0.0, 0.0, 0.7) textColorDisabled = core.Color(0.3, 0.3, 0.3) outlineColor = None outlineColorHighlighted = None outlineColorActivated = None outlineColorDisabled = None backgroundColor = core.Color(1, 1, 1) fontAttributes = font.attributes(14) interactOnlyInsideVisibleContentRect = True minimumViewWidth = (radius + circleBorder) * 2 minimumViewHeight = (radius + circleBorder) * 2 def __init__(self, text, group, *args, **kwargs): super().__init__(*args, **kwargs) self.text = str(text) self.group = group self.__selected = False self.__mouseHover = False self.__activated = False self.__capturedMouseId = None self.__interactFrame = None def onLoaded(self): super().onLoaded() def onUnload(self): super().onUnload() def onRender(self, renderer): super().onRender(renderer) state = self.STATE_DISABLED if not self.enabled else \ self.STATE_ACTIVATED if self.__activated else \ self.STATE_HIGHLIGHTED if self.__mouseHover else \ self.STATE_NORMAL bounds = self.contentBounds() # draw circle circleRect = core.Rect(bounds.x + self.circleBorder + self.leftMargin, bounds.y + bounds.height * 0.5 - self.radius, self.radius * 2, self.radius * 2) circleRect.x = math.floor( circleRect.x * self.scaleFactor) / self.scaleFactor circleRect.y = math.floor( circleRect.y * self.scaleFactor) / self.scaleFactor if self.circleBorder > 0: rc = core.Rect(circleRect) rc.origin = rc.x - self.circleBorder, rc.y - self.circleBorder rc.size = rc.width + self.circleBorder * 2, rc.height + self.circleBorder * 2 with renderer.contextForSolidEllipses(self.circleBorderColor) as r: r.add(rc) circleColor = (self.circleColor, self.circleColorHighlighted, self.circleColorActivated, self.circleColorDisabled)[state] with renderer.contextForSolidEllipses(circleColor) as r: r.add(circleRect) if self.__selected: r = self.radius - self.innerCircleRadius innerCircleRect = core.Rect(circleRect.x + r, circleRect.y + r, self.innerCircleRadius * 2, self.innerCircleRadius * 2) innerCircleColor = (self.innerCircleColor, self.innerCircleColorHighlighted, self.innerCircleColorActivated, self.innerCircleColorDisabled)[state] with renderer.contextForSolidEllipses( innerCircleColor, blend=blendstate.defaultOpaque) as r: r.add(innerCircleRect) # draw text textRect = core.Rect( circleRect.x + circleRect.width + self.circleBorder + self.padding, bounds.y, 0, bounds.height) lineWidth = self.font.lineWidth(self.text) lineHeight = self.font.lineHeight() textRect.width = min( bounds.x + bounds.width - textRect.x - self.rightMargin, lineWidth) # with renderer.contextForSolidRects(core.Color(1,0,0), blend=blendstate.defaultOpaque) as r: # r.add(textRect) textColor = (self.textColor, self.textColorHighlighted, self.textColorActivated, self.textColorDisabled)[state] outlineColor = (self.outlineColor, self.outlineColorHighlighted, self.outlineColorActivated, self.outlineColorDisabled)[state] font.drawText(renderer, textRect, self.text, self.font, textColor, outlineColor, align=font.ALIGN_LEFT, linebreak=font.LINE_BREAK_TRUNCATING_TAIL, blend=blendstate.defaultAlpha) if self.interactOnlyInsideVisibleContentRect: x1 = circleRect.x - self.circleBorder - self.padding x2 = textRect.x + textRect.width + self.padding textOriginY = bounds.y + (bounds.height - lineHeight) * 0.5 y1 = min(circleRect.y - self.circleBorder, textOriginY) - self.padding y2 = max(circleRect.y + circleRect.height, textOriginY + lineHeight) + self.padding self.__interactFrame = core.Rect(x1, y1, x2 - x1, y2 - y1) else: self.__interactFrame = bounds def onMouseDown(self, deviceId, buttonId, pos): super().onMouseDown(deviceId, buttonId, pos) if self.__capturedMouseId: if not self.isMouseCapturedBySelf(self.__capturedMouseId[0]): self.__capturedMouseId = None if self.__capturedMouseId is None: if self.__interactFrame and self.__interactFrame.isInside(pos): self.captureMouse(deviceId) self.__capturedMouseId = (deviceId, buttonId) self.__activated = True self.redraw() def onMouseUp(self, deviceId, buttonId, pos): super().onMouseUp(deviceId, buttonId, pos) if self.__capturedMouseId and self.__capturedMouseId == (deviceId, buttonId): self.releaseMouse(deviceId) if self.__interactFrame and self.__interactFrame.isInside(pos): self.setSelected() self.__capturedMouseId = None self.__activated = False self.redraw() def onMouseMove(self, deviceId, pos, delta): super().onMouseMove(deviceId, pos, delta) if self.__capturedMouseId and self.__capturedMouseId[0] == deviceId: act = self.__activated if self.isMouseCapturedBySelf(deviceId): if self.__interactFrame: self.__activated = self.__interactFrame.isInside(pos) else: self.__activated = False else: self.__capturedMouseId = None self.__activated = False if act != self.__activated: self.redraw() elif deviceId == 0: h = self.__mouseHover self.__mouseHover = self.__interactFrame.isInside( pos) if self.__interactFrame else False if self.__mouseHover != h: self.redraw() def onMouseLeave(self, deviceId): super().onMouseLeave(deviceId) if deviceId == 0 and self.__mouseHover: self.__mouseHover = False self.redraw() def siblings(self): parent = self.parent() s = [] if parent and self.group is not None: for c in parent.children(): if c is not self and isinstance(c, RadioButton): if c.group == self.group: s.append(c) return s @property def selected(self): return self.__selected def setSelected(self): if not self.__selected: self.__selected = True parent = self.parent() if parent and self.group is not None: for c in parent.children(): if c is not self and isinstance(c, RadioButton): if c.group == self.group: if c.__selected: c.__selected = False c.redraw() self.redraw() # post event screen = self.screen() if screen: screen.postOperation(self.postEvent, ()) def postEvent(self): super().invokeAllTargets(self)
def onRender(self, renderer): border = round(self.borderWidth) border2 = border * 2 bounds = self.contentBounds() clearColor = core.Color(0.0, 0.0, 0.0, 0.0) renderer.clear(clearColor) size = math.floor(min(bounds.width, bounds.height)) circleRect = core.Rect(0, 0, size, size) circleRect.center = bounds.center if border > 0: with renderer.contextForSolidEllipses( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add(circleRect) size -= border2 circleRect.size = size, size circleRect.origin = circleRect.x + border, circleRect.y + border if size > 0: if self.__csLayerMask is None: rt = rendertarget.RenderTarget(size * self.scaleFactor, size * self.scaleFactor, rendertarget.DEPTH_NONE) rd = Renderer(rt) rd.clear(core.Color(0.0, 0.0, 0.0, 0.0)) with rd.contextForSolidEllipses( core.Color(1, 1, 1, 1), blend=blendstate.defaultOpaque) as r: r.add(core.Rect(0, 0, 1, 1)) self.__csLayerMask = rt.colorTextureAtIndex(0) prog = self._progress / (self.maximumValue - self.minimumValue) if prog < 0.0: prog = 0.0 if prog > 1.0: prog = 1.0 if prog <= 0.0: with renderer.contextForSolidEllipses( self.backgroundColor, blend=blendstate.defaultOpaque) as r: r.add(circleRect) elif prog >= 1.0: with renderer.contextForSolidEllipses( self.progressColor, blend=blendstate.defaultOpaque) as r: r.add(circleRect) else: prog *= math.pi * 2 normalizedCircularPos = core.Point( math.sin(prog) * 0.5 + 0.5, math.cos(prog) * 0.5 + 0.5) # 0.0~1.0 texSize = self.__csLayerMask.width / self.scaleFactor, self.__csLayerMask.height / self.scaleFactor offset = round(circleRect.x + (circleRect.width - texSize[0]) * 0.5), round( circleRect.y + (circleRect.height - texSize[1]) * 0.5) with renderer.contextForSolidEllipses( self.backgroundColor, blend=blendstate.defaultOpaque) as r: r.add(circleRect) with renderer.contextForTexturedTriangleFan( self.__csLayerMask, color=self.progressColor, blend=blendstate.defaultAlpha) as r: _pt1 = core.Point() _pt2 = core.Point() def addTexPos(x, y): _pt1.tuple = x * texSize[0] + offset[ 0], y * texSize[1] + offset[1] _pt2.tuple = x, y r.add(_pt1, _pt2) addTexPos(0.5, 0.5) addTexPos(0.5, 1.0) if prog < math.pi * 0.5: addTexPos(normalizedCircularPos.x, 1.0) addTexPos(normalizedCircularPos.x, normalizedCircularPos.y) else: addTexPos(1.0, 1.0) addTexPos(1.0, 0.5) if prog < math.pi: addTexPos(1.0, normalizedCircularPos.y) addTexPos(normalizedCircularPos.x, normalizedCircularPos.y) else: addTexPos(1.0, 0.0) addTexPos(0.5, 0.0) if prog < math.pi * 1.5: addTexPos(normalizedCircularPos.x, 0.0) addTexPos(normalizedCircularPos.x, normalizedCircularPos.y) else: addTexPos(0.0, 0.0) addTexPos(0.0, 0.5) if prog < math.pi * 2.0: addTexPos(0.0, normalizedCircularPos.y) addTexPos(normalizedCircularPos.x, normalizedCircularPos.y) innerCircleSize = math.floor( min(self.innerCircleRadius * 2, size - border2)) if innerCircleSize > 0: innerCircleRect = core.Rect(0, 0, innerCircleSize, innerCircleSize) innerCircleRect.center = bounds.center if border > 0: with renderer.contextForSolidEllipses( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add(innerCircleRect) innerCircleRect.origin = innerCircleRect.x + border, innerCircleRect.y + border innerCircleRect.size = innerCircleRect.width - border2, innerCircleRect.height - border2 innerCircleSize -= border2 if innerCircleSize > 0: with renderer.contextForSolidEllipses( clearColor, blend=blendstate.defaultOpaque) as r: r.add(innerCircleRect)
class AnimatedProgressView(view.View): progressAnimation = 0.5 borderWidth = 1 borderColor = core.Color(0.0, 0.0, 0.0) backgroundColor = core.Color(0.5, 0.5, 0.5) progressColor = core.Color(1.0, 1.0, 1.0) minimumViewHeight = borderWidth * 2 + 1 minimumViewWidth = borderWidth * 2 + 1 def __init__(self, initialValue=0.0, minValue=0.0, maxValue=1.0, *args, **kwargs): super().__init__(*args, **kwargs) self._progress = initialValue self.minimumValue = minValue self.maximumValue = maxValue self.__animatedValue = None @property def progress(self): if self.__animatedValue: return self.__animatedValue.end return self._progress def setProgress(self, value, animate=True): if value < self.minimumValue: value = self.minimumValue elif value > self.maximumValue: value = self.maximumValue if self._progress != value and animate and self.progressAnimation > 0.0: anim = _AnimatedProgressValue() anim.begin = self._progress anim.end = value anim.length = self.progressAnimation anim.elapsed = 0.0 self.__animatedValue = anim else: self._progress = value self.__animatedValue = None def onUnload(self): super().onUnload() self._progress = self.progress self.__animatedValue = None def onUpdate(self, delta, tick, date): super().onUpdate(delta, tick, date) if self.__animatedValue: if self.__animatedValue.length > 0.0: self.__animatedValue.elapsed += delta t = self.__animatedValue.elapsed / self.__animatedValue.length t1 = 0 if t < 0.0 else 1.0 if t > 1.0 else t t2 = 1.0 - t1 value = self.__animatedValue.end * t1 + self.__animatedValue.begin * t2 if value < self.minimumValue: value = self.minimumValue elif value > self.maximumValue: value = self.maximumValue self._progress = value if self.__animatedValue.elapsed >= self.__animatedValue.length: self.__animatedValue = None else: value = self.__animatedValue.end if value < self.minimumValue: value = self.minimumValue elif value > self.maximumValue: value = self.maximumValue self._progress = value self.__animatedValue = None self.redraw() def onRender(self, renderer): border = round(self.borderWidth) border2 = border * 2 bounds = self.bounds() rc = core.Rect(bounds.x + border, bounds.y + border, bounds.width - border2, bounds.height - border2) prog = self._progress / (self.maximumValue - self.minimumValue) if prog < 0.0: prog = 0.0 if prog > 1.0: prog = 1.0 rc.width = rc.width * prog if border > 0: renderer.clear(self.borderColor) with renderer.contextForSolidRects( self.progressColor, blend=blendstate.defaultOpaque) as r: r.add(rc) rc.x += rc.width rc.width = (bounds.width - border2) - rc.width with renderer.contextForSolidRects( self.backgroundColor, blend=blendstate.defaultOpaque) as r: r.add(rc) else: renderer.clear(self.backgroundColor) with renderer.contextForSolidRects( self.progressColor, blend=blendstate.defaultOpaque) as r: r.add(rc)
class Button(label.Label, control.Control, imageview.ImageView): fontAttributes = font.attributes(12) backgroundColor = core.Color(0.85, 0.85, 0.85) backgroundColorHighlighted = core.Color(0.9, 0.9, 1.0) backgroundColorActivated = core.Color(0.5, 0.5, 0.5) backgroundColorDisabled = core.Color(0.4, 0.4, 0.4) textColor = core.Color(0.0, 0.0, 0.0) textColorHighlighted = core.Color(0.0, 0.0, 0.0) textColorActivated = core.Color(1.0, 1.0, 1.0) textColorDisabled = core.Color(0.3, 0.3, 0.3) outlineColor = None outlineColorHighlighted = core.Color(1.0, 1.0, 1.0, 1.0) outlineColorActivated = core.Color(0.0, 0.0, 0.0, 1.0) outlineColorDisabled = None backgroundImage = None backgroundImageHighlighted = None backgroundImageActivated = None backgroundImageDisabled = None backgroundImageTransform = core.Matrix3() borderColor = core.Color(0.0, 0.0, 0.0, 1.0) borderWidth = 1 def __init__(self, text='Button', *args, **kwargs): super().__init__(text=text, *args, **kwargs) self.buttonPressed = False self.__mouseHover = False self.__capturedMouseId = None if self.textureImage: self.backgroundImage = self.textureImage def setTextColor(self, color, state=control.Control.STATE_ALL): assert isinstance(color, core.Color) if state in (self.STATE_ALL, self.STATE_NORMAL): self.textColor = color if state in (self.STATE_ALL, self.STATE_HIGHLIGHTED): self.textColorHighlighted = color if state in (self.STATE_ALL, self.STATE_ACTIVATED): self.textColorActivated = color if state in (self.STATE_ALL, self.STATE_DISABLED): self.textColorDisabled = color def setOutlineColor(self, color, state=control.Control.STATE_ALL): assert isinstance(color, core.Color) if state in (self.STATE_ALL, self.STATE_NORMAL): self.outlineColor = color if state in (self.STATE_ALL, self.STATE_HIGHLIGHTED): self.outlineColorHighlighted = color if state in (self.STATE_ALL, self.STATE_ACTIVATED): self.outlineColorActivated = color if state in (self.STATE_ALL, self.STATE_DISABLED): self.outlineColorDisabled = color def onLoaded(self): super().onLoaded() def onUnload(self): self.backgroundImage = None self.backgroundImageHighlighted = None self.backgroundImageActivated = None self.backgroundImageDisabled = None return super().onUnload() def onRender(self, r): state = self.STATE_DISABLED if not self.enabled else \ self.STATE_ACTIVATED if self.buttonPressed else \ self.STATE_HIGHLIGHTED if self.__mouseHover else \ self.STATE_NORMAL textColors = (self.textColor, self.textColorHighlighted, self.textColorActivated, self.textColorDisabled) outlineColors = (self.outlineColor, self.outlineColorHighlighted, self.outlineColorActivated, self.outlineColorDisabled) bgColors = (self.backgroundColor, self.backgroundColorHighlighted, self.backgroundColorActivated, self.backgroundColorDisabled) bgImages = (self.backgroundImage, self.backgroundImageHighlighted, self.backgroundImageActivated, self.backgroundImageDisabled) self.textColor = textColors[state] self.outlineColor = outlineColors[state] self.backgroundColor = bgColors[state] self.textureImage = bgImages[state] if not self.textureImage: self.textureImage = self.backgroundImage super().onRender(r) self.backgroundColor = bgColors[0] self.textColor = textColors[0] self.outlineColor = outlineColors[0] def discardAction(self): if self.__capturedMouseId: if self.isMouseCapturedBySelf(self.__capturedMouseId[0]): self.releaseMouse(self.__capturedMouseId[0]) self.__capturedMouseId = None self.buttonPressed = False self.redraw() def onMouseDown(self, deviceId, buttonId, pos): super().onMouseDown(deviceId, buttonId, pos) acceptMouse = True if self.__capturedMouseId: if not self.isMouseCapturedBySelf(self.__capturedMouseId[0]): self.__capturedMouseId = None if self.__capturedMouseId is None: self.captureMouse(deviceId) self.__capturedMouseId = (deviceId, buttonId) self.buttonPressed = True self.redraw() def onMouseUp(self, deviceId, buttonId, pos): super().onMouseUp(deviceId, buttonId, pos) if self.__capturedMouseId and self.__capturedMouseId == (deviceId, buttonId): self.releaseMouse(deviceId) if self.bounds().isInside(pos): self.screen().postOperation(self.postEvent, ()) self.__capturedMouseId = None self.buttonPressed = False self.redraw() def onMouseMove(self, deviceId, pos, delta): super().onMouseMove(deviceId, pos, delta) if self.__capturedMouseId and self.__capturedMouseId[0] == deviceId: btnPressed = self.buttonPressed if self.isMouseCapturedBySelf(deviceId): self.buttonPressed = self.bounds().isInside(pos) else: self.__capturedMouseId = None self.buttonPressed = False if btnPressed != self.buttonPressed: self.redraw() elif deviceId == 0: if not self.__mouseHover: self.__mouseHover = True if not self.buttonPressed: self.redraw() def onMouseLeave(self, deviceId): super().onMouseLeave(deviceId) if deviceId == 0 and self.__mouseHover: self.__mouseHover = False if not self.buttonPressed: self.redraw() def postEvent(self): super().invokeAllTargets(self)
def drawText(renderer, frame, text, font, textColor=core.Color(1.0, 1.0, 1.0, 1.0), outlineColor=core.Color(0, 0, 0, 0.5), scaleToFit=False, align=ALIGN_CENTER, alignToPixel=True, linebreak=LINE_BREAK_TRUNCATING_TAIL, blend=blendstate.defaultAlpha): textFont = None outlineFont = None if font: if isinstance(font, UIFont): textFont = font.textFont outlineFont = font.outlineFont elif isinstance(font, core.Font): textFont = font elif isinstance(font, (tuple, list)): c = len(font) if c > 0: textFont = font[0] if c > 1: outlineFont = font[1] else: raise TypeError( 'font argument must be Font or two Font objects tuple.') layoutFont = textFont if textFont else outlineFont if len(text) > 0 and frame.width > 0 and frame.height > 0 and layoutFont: viewport = renderer.viewport[2:] scale = renderer.bounds[2:] scaleFactor = (viewport[0] / scale[0], viewport[1] / scale[1]) localToPixel = lambda x, y: (x * scaleFactor[0], y * scaleFactor[1]) pixelToLocal = lambda x, y: (x / scaleFactor[0], y / scaleFactor[1]) pixelFrame = core.Rect(frame) pixelFrame.origin = localToPixel(*frame.origin) pixelFrame.size = localToPixel(*frame.size) text = linebreak(pixelFrame, layoutFont, text) width = layoutFont.lineWidth(text) height = layoutFont.lineHeight() baseline = layoutFont.baseline if scaleToFit: scaleX = pixelFrame.width / width scaleY = pixelFrame.height / height scale = min(scaleX, scaleY) width = width * scale height = height * scale baseline = baseline * scale begin = align(pixelFrame, width, height, baseline) if alignToPixel: x = floor(begin[0] + 0.5) y = floor(begin[1] + 0.5) begin = (x, y) end = (begin[0] + width, begin[1]) begin = core.Point(pixelToLocal(*begin)) end = core.Point(pixelToLocal(*end)) if outlineFont and outlineColor: renderer.renderTextBaseline(begin, end, text, outlineFont, outlineColor, blend) if textFont and textColor: renderer.renderTextBaseline(begin, end, text, textFont, textColor, blend)
def onLoaded(self): super().onLoaded() self.setBlendState(blendstate.defaultAlpha) self.backgroundColor = core.Color(0, 0, 0, 0) self.screen().postOperation(self.setContentViewFrameToCenter, ())
class View(frame.Frame): fontAttributes = font.attributes(12) backgroundColor = core.Color(1.0, 1.0, 1.0) borderColor = core.Color(0.0, 0.0, 0.0, 1.0) borderWidth = 0 minimumViewWidth = 1 minimumViewHeight = 1 def __init__(self, frame=core.Rect(0, 0, 1, 1), *args, **kwargs): super().__init__(*args, **kwargs) self.scaleFactor = DEFAULT_UI_SCALE assert self.scaleFactor > 0 assert isinstance(frame, core.Rect) self.layouter = None self.gestureRecognizers = [] self.setFrame(frame) self.setBlendState(blendstate.defaultOpaque) self.font = None def layout(self): if self.layouter: self.layouter(self.contentBounds(), self.children()) def onResized(self): self.contentScale = tuple(v / self.scaleFactor for v in self.contentResolution) self.surfaceVisibilityTest = not (round(self.borderWidth) > 0) self.layout() @property def frame(self): return core.Rect(self.__frame) @frame.setter def frame(self, frame): self.setFrame(frame) def setFrame(self, frame): assert isinstance(frame, core.Rect) #assert frame.width > 0 #assert frame.height > 0 assert self.minimumViewWidth > 0 assert self.minimumViewHeight > 0 width = max(frame.width, self.minimumViewWidth) height = max(frame.height, self.minimumViewHeight) frame = core.Rect(frame.origin + (width, height)) try: f = self.__frame if f == frame: return except AttributeError: pass self.__frame = frame self.contentScale = width, height linear = core.LinearTransform2() linear.scale(width, height) self.transform = core.AffineTransform2(linear, frame.origin).matrix3() def bounds(self): ''' :return: projected local bounds rect (include border) ''' w, h = self.contentScale return core.Rect(0, 0, w, h) def contentBounds(self): ''' :return: projected local bounds rect for contents (without border) ''' rc = self.bounds() border = round(self.borderWidth) border2 = border * 2 rc.size = (rc.width - border2, rc.height - border2) rc.origin = (rc.x + border, rc.y + border) return rc def displayBounds(self): ''' :return: unprojected local bounds rect (include border) ''' return self.unprojectLocalRect(self.bounds()) def contentDisplayBounds(self): ''' :return: unprojected local bounds rect for contents (without border) ''' return self.unprojectLocalRect(self.contentBounds()) def onRender(self, renderer): border = round(self.borderWidth) if border > 0: renderer.clear(self.borderColor) bounds = View.contentBounds(self) pixelBounds = self.convertLocalToPixel( self.unprojectLocalRect(bounds)) renderer.viewport = pixelBounds.tuple renderer.bounds = bounds.tuple _tm = renderer.transform renderer.transform = core.Matrix3() with renderer.contextForSolidRects( self.backgroundColor, blend=blendstate.defaultOpaque) as r: r.add(bounds) renderer.transform = _tm else: renderer.clear(self.backgroundColor) def onUpdate(self, delta, tick, date): pass def onLoaded(self): super().onLoaded() self.contentScale = tuple(v / self.scaleFactor for v in self.contentResolution) border2 = round(self.borderWidth) * 2 minWidth = border2 + 1 minHeight = border2 + 1 self.minimumViewWidth = max(minWidth, self.minimumViewWidth) self.maximumViewHeight = max(minHeight, self.minimumViewHeight) if not self.font: self.font = font.loadUIFont(self.fontAttributes, self.scaleFactor) frame = self.frame if frame.width < self.minimumViewWidth: frame.height = self.minimumViewWidth if frame.height < self.minimumViewHeight: frame.height = self.minimumViewHeight self.frame = frame def onUnload(self): self.font = None screen = self.screen() if screen and screen.frame == self: resource.clear() super().onUnload() def getResolution(self): return tuple(v * self.scaleFactor for v in self.__frame.size) def contentHitTest(self, pos): return self.contentDisplayBounds().isInside(pos) def captureMouse(self, deviceId): r = super().captureMouse(deviceId) screen = self.screen() if screen: keyboardId = 0 kf = screen.keyFrame(keyboardId) if kf and not kf.isDescendantOf(self): self.captureKeyboard(keyboardId) self.releaseKeyboard(keyboardId) return r
class Checkbox(control.Control, view.View): boxSize = 14 boxBorder = 1 boxBorderColor = core.Color(0, 0, 0) leftMargin = 4 rightMargin = 0 padding = 8 boxColor = core.Color(1.0, 1.0, 1.0) boxColorHighlighted = core.Color(0.8, 0.8, 1.0) boxColorActivated = core.Color(0.3, 0.3, 1.0) boxColorDisabled = core.Color(0.6, 0.6, 0.6) checkColor = core.Color(0.0, 0.0, 0.0) checkColorHighlighted = core.Color(0.0, 0.0, 1.0) checkColorActivated = core.Color(0.0, 1.0, 1.0) checkColorDisabled = core.Color(0.2, 0.2, 0.2) textColor = core.Color(0.0, 0.0, 0.0) textColorHighlighted = core.Color(0.0, 0.0, 0.0) textColorActivated = core.Color(0.0, 0.0, 0.7) textColorDisabled = core.Color(0.3, 0.3, 0.3) outlineColor = None outlineColorHighlighted = None outlineColorActivated = None outlineColorDisabled = None backgroundColor = core.Color(1, 1, 1) fontAttributes = font.attributes(14) interactOnlyInsideVisibleContentRect = True minimumViewHeight = boxSize + boxBorder * 2 minimumViewWidth = boxSize + leftMargin + boxBorder * 2 def __init__(self, text='', value=False, *args, **kwargs): super().__init__(*args, **kwargs) self.text = str(text) self.value = value self.__mouseHover = False self.__activated = False self.__capturedMouseId = None self.__interactFrame = None def onLoaded(self): super().onLoaded() def onUnload(self): super().onUnload() def onRender(self, renderer): super().onRender(renderer) invScale = 1.0 / self.scaleFactor state = self.STATE_DISABLED if not self.enabled else \ self.STATE_ACTIVATED if self.__activated else \ self.STATE_HIGHLIGHTED if self.__mouseHover else \ self.STATE_NORMAL bounds = self.contentBounds() # draw box boxRect = core.Rect(bounds.x + self.leftMargin, bounds.y + (bounds.height - self.boxSize) * 0.5, self.boxSize, self.boxSize) if self.boxBorder > 0: border2 = self.boxBorder * 2 boxRect.x += self.boxBorder rc = core.Rect(boxRect.x - self.boxBorder, boxRect.y - self.boxBorder, boxRect.width + border2, boxRect.height + border2) with renderer.contextForSolidRects( self.boxBorderColor, blend=blendstate.defaultOpaque) as r: r.add(rc) boxColor = (self.boxColor, self.boxColorHighlighted, self.boxColorActivated, self.boxColorDisabled)[state] with renderer.contextForSolidRects( boxColor, blend=blendstate.defaultOpaque) as r: r.add(boxRect) # draw check-mark if self.value: p0 = core.Point(0.15, 0.6) p1 = core.Point(0.05, 0.5) p2 = core.Point(0.4, 0.35) p3 = core.Point(0.4, 0.15) p4 = core.Point(0.85, 0.8) p5 = core.Point(0.95, 0.7) for p in (p0, p1, p2, p3, p4, p5): p.x = p.x * boxRect.width + boxRect.x p.y = p.y * boxRect.height + boxRect.y checkColor = (self.checkColor, self.checkColorHighlighted, self.checkColorActivated, self.checkColorDisabled)[state] with renderer.contextForSolidTriangleStrip( checkColor, blend=blendstate.defaultOpaque) as r: r.addTriangle(p0, p1, p2) r.add(p3) r.add(p4) r.add(p5) # draw text textRect = core.Rect( boxRect.x + boxRect.width + self.boxBorder + self.padding, bounds.y, 0, bounds.height) lineWidth = self.font.lineWidth(self.text) * invScale lineHeight = self.font.lineHeight() * invScale textRect.width = min( bounds.x + bounds.width - textRect.x - self.rightMargin, lineWidth) # with renderer.contextForSolidRects(core.Color(1,0,0), blend=blendstate.defaultOpaque) as r: # r.add(textRect) textColor = (self.textColor, self.textColorHighlighted, self.textColorActivated, self.textColorDisabled)[state] outlineColor = (self.outlineColor, self.outlineColorHighlighted, self.outlineColorActivated, self.outlineColorDisabled)[state] font.drawText(renderer, textRect, self.text, self.font, textColor, outlineColor, align=font.ALIGN_LEFT, linebreak=font.LINE_BREAK_TRUNCATING_TAIL, blend=blendstate.defaultAlpha) if self.interactOnlyInsideVisibleContentRect: x1 = boxRect.x - self.boxBorder - self.padding x2 = textRect.x + textRect.width + self.padding textOriginY = bounds.y + (bounds.height - lineHeight) * 0.5 y1 = min(boxRect.y - self.boxBorder, textOriginY) - self.padding y2 = max(boxRect.y + boxRect.height, textOriginY + lineHeight) + self.padding self.__interactFrame = core.Rect(x1, y1, x2 - x1, y2 - y1) else: self.__interactFrame = bounds def onMouseDown(self, deviceId, buttonId, pos): super().onMouseDown(deviceId, buttonId, pos) if self.__capturedMouseId: if not self.isMouseCapturedBySelf(self.__capturedMouseId[0]): self.__capturedMouseId = None if self.__capturedMouseId is None: if self.__interactFrame and self.__interactFrame.isInside(pos): self.captureMouse(deviceId) self.__capturedMouseId = (deviceId, buttonId) self.__activated = True self.redraw() def onMouseUp(self, deviceId, buttonId, pos): super().onMouseUp(deviceId, buttonId, pos) if self.__capturedMouseId and self.__capturedMouseId == (deviceId, buttonId): self.releaseMouse(deviceId) if self.__interactFrame and self.__interactFrame.isInside(pos): self.value = not self.value self.screen().postOperation(self.postEvent, ()) self.__capturedMouseId = None self.__activated = False self.redraw() def onMouseMove(self, deviceId, pos, delta): super().onMouseMove(deviceId, pos, delta) if self.__capturedMouseId and self.__capturedMouseId[0] == deviceId: act = self.__activated if self.isMouseCapturedBySelf(deviceId): if self.__interactFrame: self.__activated = self.__interactFrame.isInside(pos) else: self.__activated = False else: self.__capturedMouseId = None self.__activated = False if act != self.__activated: self.redraw() elif deviceId == 0: h = self.__mouseHover self.__mouseHover = self.__interactFrame.isInside( pos) if self.__interactFrame else False if self.__mouseHover != h: self.redraw() def onMouseLeave(self, deviceId): super().onMouseLeave(deviceId) if deviceId == 0 and self.__mouseHover: self.__mouseHover = False self.redraw() def postEvent(self): super().invokeAllTargets(self)
def randomRectSource(self): return core.Size(random.randrange(16, 256), random.randrange(16, 256)), core.Color( random.random(), random.random(), random.random())
class TitledView(view.View): caption = '' captionLeftMargin = 2 captionRightMargin = 2 captionTopMargin = 4 captionBottomMargin = 1 captionHeight = 28 contentBorder = 1 contentMargin = 4 borderWidth = 1 movable = True pixelBasedMove = True clampToParentEdge = True fontAttributes = font.attributes(16) titleTextColor = core.Color(0, 0, 0) titleTextColorHighlighted = core.Color(0, 0, 0) titleTextColorActivated = core.Color(1.0, 1.0, 1.0) titleTextColorDisabled = core.Color(0.3, 0.3, 0.3) titleOutlineColor = None titleOutlineColorHighlighted = core.Color(1.0, 1.0, 1.0, 0.75) titleOutlineColorActivated = core.Color(0.2, 0.2, 0.2, 0.84) titleOutlineColorDisabled = None outerBoundColor = core.Color(0.9, 0.9, 0.9) outerBoundColorHighlighted = core.Color(0.8, 0.8, 1.0) outerBoundColorActivated = core.Color(0.6, 0.6, 1.0) outerBoundColorDisabled = core.Color(0.8, 0.8, 0.8) minimumViewWidth = borderWidth * 2 + contentBorder * 2 + contentMargin * 2 + 1 minimumViewHeight = borderWidth * 2 + contentBorder * 2 + contentMargin * 2 + captionHeight + 1 def __init__(self, caption=None, *args, **kwargs): super().__init__(*args, **kwargs) if caption is not None: self.caption = str(caption) self.__mouseTrackInfo = None self.__mouseHover = False def orderFront(self): parent = self.parent() if parent: parent.bringChildToFront(self) def titleFrame(self): border = round(self.contentBorder) margin = round(self.contentMargin) bounds = super().contentBounds() y2 = bounds.y + bounds.height y1 = bounds.y + bounds.height - self.captionHeight bounds.y = y1 bounds.height = y2 - y1 return bounds def contentBounds(self): border = round(self.contentBorder) margin = round(self.contentMargin) bounds = super().contentBounds() y1 = bounds.y + margin + border y2 = bounds.y + bounds.height - self.captionHeight - margin - border x1 = bounds.x + margin + border x2 = bounds.x + bounds.width - margin - border return core.Rect(x1, y1, x2 - x1, y2 - y1) def onLoaded(self): super().onLoaded() b2 = round(self.borderWidth) * 2 cb2 = round(self.contentBorder) * 2 m2 = round(self.contentMargin) * 2 self.minimumViewWidth = b2 + cb2 + m2 + 1 self.minimumViewHeight = b2 + cb2 + m2 + self.captionHeight + 1 self.surfaceVisibilityTest = False def onUnload(self): super().onUnload() def __frameClippedByParentBounds(self, frame): parent = self.parent() if parent: try: bounds = parent.contentBounds() except AttributeError: bounds = parent.bounds() # make a copy of frame frame = core.Rect(frame) if frame.width > bounds.width: frame.x = bounds.x elif frame.x + frame.width > bounds.x + bounds.width: frame.x = bounds.x + bounds.width - frame.width elif frame.x < bounds.x: frame.x = bounds.x if frame.height > bounds.height: frame.y = bounds.y + bounds.height - frame.height elif frame.y + frame.height > bounds.y + bounds.height: frame.y = bounds.y + bounds.height - frame.height elif frame.y < bounds.y: frame.y = bounds.y return frame def moveToParentInside(self): frame = self.__frameClippedByParentBounds(self.frame) if frame != self.frame: self.frame = frame def onUpdate(self, delta, tick, date): super().onUpdate(delta, tick, date) if self.clampToParentEdge: frame = self.__frameClippedByParentBounds(self.frame) if frame != self.frame: self.frame = frame def onRender(self, renderer): c = self.backgroundColor if self.enabled: if self.__mouseTrackInfo: self.backgroundColor = self.outerBoundColorActivated textColor = self.titleTextColorActivated outlineColor = self.titleOutlineColorActivated elif self.__mouseHover: self.backgroundColor = self.outerBoundColorHighlighted textColor = self.titleTextColorHighlighted outlineColor = self.titleOutlineColorHighlighted else: self.backgroundColor = self.outerBoundColor textColor = self.titleTextColor outlineColor = self.titleOutlineColor else: self.backgroundColor = self.outerBoundColorDisabled textColor = self.titleTextColorDisabled outlineColor = self.titleOutlineColorDisabled super().onRender(renderer) self.backgroundColor = c _tm = renderer.transform renderer.transform = core.Matrix3() titleRect = self.titleFrame() titleRect.x += self.captionLeftMargin titleRect.width -= self.captionLeftMargin + self.captionRightMargin titleRect.y += self.captionBottomMargin titleRect.height -= self.captionTopMargin + self.captionBottomMargin font.drawText(renderer, titleRect, self.caption, self.font, textColor, outlineColor, align=font.ALIGN_BOTTOM) bounds = TitledView.contentBounds(self) border = round(self.contentBorder) if border > 0: rc = core.Rect(bounds) rc.x -= border rc.width += border * 2 rc.y -= border rc.height += border * 2 with renderer.contextForSolidRects( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add(rc) with renderer.contextForSolidRects( self.backgroundColor, blend=blendstate.defaultOpaque) as r: r.add(bounds) pixelBounds = self.convertLocalToPixel(self.unprojectLocalRect(bounds)) renderer.viewport = pixelBounds.tuple renderer.bounds = bounds.tuple renderer.transform = _tm def onMouseDown(self, deviceId, buttonId, pos): super().onMouseDown(deviceId, buttonId, pos) if buttonId == 0 and not self.__mouseTrackInfo: self.screen().postOperation(self.orderFront, ()) bounds = self.bounds() contentBounds = TitledView.contentBounds(self) pos = self.projectLocalPoint(pos) if bounds.isInside(pos) and not contentBounds.isInside(pos): self.__mouseTrackInfo = _MouseTrackInfo(deviceId, pos) self.captureMouse(deviceId) self.redraw() def onMouseUp(self, deviceId, buttonId, pos): super().onMouseUp(deviceId, buttonId, pos) if buttonId == 0 and self.__mouseTrackInfo and self.__mouseTrackInfo.deviceId == deviceId: self.__mouseTrackInfo = None self.releaseMouse(deviceId) self.redraw() if deviceId == 0 and not self.__mouseHover: bounds = self.bounds() contentBounds = self.contentBounds() pos = self.projectLocalPoint(pos) if bounds.isInside(pos) and not contentBounds.isInside(pos): self.__mouseHover = True def onMouseMove(self, deviceId, pos, delta): super().onMouseMove(deviceId, pos, delta) if self.__mouseTrackInfo and self.__mouseTrackInfo.deviceId == deviceId: if self.isMouseCapturedBySelf(deviceId): if self.movable: parent = self.parent() if parent: bounds = parent.contentDisplayBounds() v1 = self.unprojectLocalPoint( self.__mouseTrackInfo.pos).vector() v2 = pos.vector() tm = self.localToParentTransformMatrix() v1.transform(tm) v2.transform(tm) x1, x2 = bounds.x, bounds.x + bounds.width y1, y2 = bounds.y, bounds.y + bounds.height v2.x = x1 if v2.x < x1 else x2 if v2.x > x2 else v2.x v2.y = y1 if v2.y < y1 else y2 if v2.y > y2 else v2.y offset = v2 - v1 frame = self.frame frame.origin = frame.x + offset.x, frame.y + offset.y if self.clampToParentEdge: frame = self.__frameClippedByParentBounds(frame) if self.pixelBasedMove: p = parent.convertLocalToPixel( core.Point(frame.origin)) p.tuple = round(p.x), round(p.y) frame.origin = parent.convertPixelToLocal(p) if frame != self.frame: self.screen().postOperation( self.setFrame, (frame, )) else: # we lost mouse self.__mouseTrackInfo = None self.redraw() elif deviceId == 0: # process hover if not self.__mouseHover: self.__mouseHover = True if not self.__mouseTrackInfo: self.redraw() def onMouseLeave(self, deviceId): super().onMouseLeave(deviceId) if deviceId == 0 and self.__mouseHover: self.__mouseHover = False if not self.__mouseTrackInfo: self.redraw() def frameForContentFrame(self, frame): frameBorder = round(self.borderWidth) contentBorder = round(self.contentBorder) margin = round(self.contentMargin) x1 = frame.x - (margin + contentBorder + frameBorder) x2 = frame.x + frame.width + (margin + contentBorder + frameBorder) y1 = frame.y - (margin + contentBorder + frameBorder) y2 = frame.y + frame.height + (margin + contentBorder + frameBorder + self.captionHeight) return core.Rect(x1, y1, x2 - x1, y2 - y1)
class ScrollView(view.View): scrollBarBorderWidth = 1 scrollBarSize = 16 scrollSliderMinimumLength = 10 showVerticalScrollBar = False showHorizontalScrollBar = False showZoomButton = False leftScrollBar = False minimumZoomScale = 0.1 maximumZoomScale = 10 _activatedColor = core.Color(0.6, 0.6, 1.0) _highlightedColor = core.Color(0.8, 0.8, 1.0) _disabledColor = core.Color(0.4, 0.4, 0.4) scrollTrackColor = core.Color(0.5, 0.5, 0.5) scrollTrackColorHighlighted = core.Color(0.65, 0.65, 0.65) scrollTrackColorDisabled = _disabledColor scrollSliderColor = core.Color(0.87, 0.87, 0.87) scrollSliderColorHighlighted = _highlightedColor scrollSliderColorActivated = _activatedColor zoomButtonColor = core.Color(0.87, 0.87, 0.87) zoomButtonColorHighlighted = _highlightedColor zoomButtonColorActivated = _activatedColor zoomButtonColorDisabled = _disabledColor def __init__(self, contentSize=None, *args, **kwargs): super().__init__(*args, **kwargs) self.__zoomScale = 1.0 self.__contentOffset = core.Point(0, 0) if contentSize: assert isinstance(contentSize, core.Size) scaleX, scaleY = self.contentScale width = max(contentSize.width, scaleX) height = max(contentSize.height, scaleY) self.__contentSize = core.Size(width, height) else: self.__contentSize = core.Size(0, 0) self.__verticalScrollTrackRect = None self.__verticalScrollSliderRect = None self.__horizontalScrollTrackRect = None self.__horizontalScrollSliderRect = None self.__zoomButtonRect = None self.__mouseHoverPos = 0 self.__mouseTrackInfo = None self.__updateContentTransform = False @property def contentOffset(self): return self.__contentOffset @contentOffset.setter def contentOffset(self, value): self.__contentOffset = value self.__updateContentTransform = True @property def contentSize(self): return self.__contentSize @contentSize.setter def contentSize(self, value): self.__contentSize = value self.__updateContentTransform = True @property def zoomScale(self): return self.__zoomScale @zoomScale.setter def zoomScale(self, value): self.__zoomScale = value self.__updateContentTransform = True def layout(self): if self.layouter: self.layouter(self.contentBounds(), self.children()) def onLoaded(self): super().onLoaded() self._updateScrollTrackRect() self.updateContentTransform() def contentBounds(self): """ calculate actual display bounds. bounds NOT INCLUDES scroll-bar area!! """ border = round(self.scrollBarBorderWidth) bounds = super().contentBounds() if bounds.width < border + self.scrollBarSize: self.showVerticalScrollBar = False if bounds.height < border + self.scrollBarSize: self.showHorizontalScrollBar = False if self.showVerticalScrollBar: bounds.width -= self.scrollBarSize + border if self.leftScrollBar: bounds.x += self.scrollBarSize + border if self.showHorizontalScrollBar: bounds.height -= self.scrollBarSize + border bounds.y += self.scrollBarSize + border return bounds def contentDisplayBounds(self): """ calculate content-bounds NOT INCLUDES scroll-bar area. result bounds is not same as visible area. (applied with scale, includes scrollable area) """ border = round(self.scrollBarBorderWidth) bounds = super().contentBounds() bounds = super().unprojectLocalRect(bounds) if bounds.width < border + self.scrollBarSize: self.showVerticalScrollBar = False if bounds.height < border + self.scrollBarSize: self.showHorizontalScrollBar = False if self.showVerticalScrollBar: bounds.width -= self.scrollBarSize + border if self.leftScrollBar: bounds.x += self.scrollBarSize + border if self.showHorizontalScrollBar: bounds.height -= self.scrollBarSize + border bounds.y += self.scrollBarSize + border return bounds def maxScrollOffset(self): bounds = self.contentBounds() scaleX = bounds.width / self.__zoomScale scaleY = bounds.height / self.__zoomScale maxX = max(self.__contentSize.width - scaleX, 0) maxY = max(self.__contentSize.height - scaleY, 0) return maxX, maxY def _updateScrollSliderRect(self): self.__verticalScrollSliderRect = None self.__horizontalScrollSliderRect = None if self.showVerticalScrollBar or self.showHorizontalScrollBar: # print('_updateScrollSliderRect ({})'.format(core.Timer.tick())) bounds = self.contentBounds() if self.showVerticalScrollBar: scale = bounds.height / self.__zoomScale scrollMax = self.__contentSize.height - scale if scrollMax > 0 and self.__verticalScrollTrackRect.height > 2: rc = core.Rect(self.__verticalScrollTrackRect) pos = self.__contentOffset.y / scrollMax height = rc.height sliderMinLength = self.scrollSliderMinimumLength if sliderMinLength >= rc.height: sliderMinLength = rc.height * 0.5 rc.height = ( rc.height - sliderMinLength ) * scale / self.__contentSize.height + sliderMinLength rc.y += (height - rc.height) * pos self.__verticalScrollSliderRect = rc if self.showHorizontalScrollBar: scale = bounds.width / self.__zoomScale scrollMax = self.__contentSize.width - scale if scrollMax > 0 and self.__horizontalScrollTrackRect.height > 2: rc = core.Rect(self.__horizontalScrollTrackRect) pos = self.__contentOffset.x / scrollMax width = rc.width sliderMinLength = self.scrollSliderMinimumLength if sliderMinLength >= rc.width: sliderMinLength = rc.width * 0.5 rc.width = ( rc.width - sliderMinLength ) * scale / self.__contentSize.width + sliderMinLength rc.x += (width - rc.width) * pos self.__horizontalScrollSliderRect = rc def _updateScrollTrackRect(self): self.__verticalScrollTrackRect = None self.__horizontalScrollTrackRect = None self.__zoomButtonRect = None if self.showVerticalScrollBar or self.showHorizontalScrollBar: # print('_updateScrollTrackRect') border = round(self.scrollBarBorderWidth) bounds = self.contentBounds() scrollBarSize = self.scrollBarSize if self.showVerticalScrollBar: rc = core.Rect() rc.width = scrollBarSize if self.leftScrollBar: rc.x = bounds.x - scrollBarSize - border else: rc.x = bounds.x + bounds.width + border if not self.showHorizontalScrollBar and self.showZoomButton: rc.y = bounds.y + scrollBarSize + border rc.height = bounds.height - scrollBarSize else: rc.y = bounds.y rc.height = bounds.height self.__verticalScrollTrackRect = rc if self.showHorizontalScrollBar: rc = core.Rect() rc.x = bounds.x rc.y = bounds.y - scrollBarSize - border rc.height = scrollBarSize if not self.showVerticalScrollBar and self.showZoomButton: rc.width = bounds.width - scrollBarSize + border if self.leftScrollBar: rc.x = bounds.x + scrollBarSize + border else: rc.width = bounds.width self.__horizontalScrollTrackRect = rc zoomRectShouldVisible = self.showVerticalScrollBar and self.showHorizontalScrollBar if zoomRectShouldVisible or self.showZoomButton: rc = core.Rect() rc.width = scrollBarSize rc.height = scrollBarSize if self.showVerticalScrollBar: if self.leftScrollBar: rc.x = bounds.x - scrollBarSize - border else: rc.x = bounds.x + bounds.width + border else: if self.leftScrollBar: rc.x = bounds.x else: rc.x = bounds.x + bounds.width - scrollBarSize if self.showHorizontalScrollBar: rc.y = bounds.y - scrollBarSize - border else: rc.y = bounds.y self.__zoomButtonRect = rc self._updateScrollSliderRect() def onResized(self): super().onResized() self.updateContentTransform() if self.surfaceVisibilityTest: if self.showHorizontalScrollBar or self.showVerticalScrollBar: self.surfaceVisibilityTest = False self._updateScrollTrackRect() def onUpdate(self, delta, tick, date): super().onUpdate(delta, tick, date) if self.__updateContentTransform: self.updateContentTransform() def onRender(self, renderer): super().onRender(renderer) border = round(self.scrollBarBorderWidth) / self.__zoomScale border2 = border * 2 trackPos = self.__mouseTrackInfo.type if self.__mouseTrackInfo else 0 hoverPos = self.__mouseHoverPos if self.__verticalScrollTrackRect: rc = self.unprojectLocalRect(self.__verticalScrollTrackRect) if border > 0: with renderer.contextForSolidRects( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add( core.Rect(rc.x - border, rc.y - border, rc.width + border2, rc.height + border2)) color = self.scrollTrackColorHighlighted if hoverPos == _POS_VERTICAL_SCROLL_TRACK \ else self.scrollTrackColor if self.__verticalScrollSliderRect \ else self.scrollTrackColorDisabled with renderer.contextForSolidRects( color, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.__verticalScrollSliderRect: rc = self.unprojectLocalRect(self.__verticalScrollSliderRect) color = self.scrollSliderColorActivated if trackPos == _POS_VERTICAL_SCROLL_SLIDER \ else self.scrollSliderColorHighlighted if hoverPos == _POS_VERTICAL_SCROLL_SLIDER \ else self.scrollSliderColor with renderer.contextForSolidRects( color, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.__horizontalScrollTrackRect: rc = self.unprojectLocalRect(self.__horizontalScrollTrackRect) if border > 0: with renderer.contextForSolidRects( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add( core.Rect(rc.x - border, rc.y - border, rc.width + border2, rc.height + border2)) color = self.scrollTrackColorHighlighted if hoverPos == _POS_HORIZONTAL_SCROLL_TRACK \ else self.scrollTrackColor if self.__horizontalScrollSliderRect \ else self.scrollTrackColorDisabled with renderer.contextForSolidRects( color, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.__horizontalScrollSliderRect: rc = self.unprojectLocalRect(self.__horizontalScrollSliderRect) color = self.scrollSliderColorActivated if trackPos == _POS_HORIZONTAL_SCROLL_SLIDER \ else self.scrollSliderColorHighlighted if hoverPos == _POS_HORIZONTAL_SCROLL_SLIDER \ else self.scrollSliderColor with renderer.contextForSolidRects( color, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.__zoomButtonRect: rc = self.unprojectLocalRect(self.__zoomButtonRect) if border > 0: with renderer.contextForSolidRects( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add( core.Rect(rc.x - border, rc.y - border, rc.width + border2, rc.height + border2)) activated = trackPos == _POS_ZOOM_BUTTON if activated: pos = self.__mouseTrackInfo.offset + self.__zoomButtonRect.origin activated = self.__zoomButtonRect.isInside(pos) color = self.zoomButtonColorDisabled if not self.showZoomButton \ else self.zoomButtonColorActivated if activated \ else self.zoomButtonColorHighlighted if hoverPos == _POS_ZOOM_BUTTON \ else self.zoomButtonColor with renderer.contextForSolidRects( color, blend=blendstate.defaultOpaque) as r: r.add(rc) x, y = self.contentScale w, h = self.contentResolution scaleX = w / x scaleY = h / y bounds = self.contentBounds() renderer.viewport = bounds.x * scaleX, bounds.y * scaleY, bounds.width * scaleX, bounds.height * scaleY renderer.bounds = bounds def updateContentTransform(self): clamp = lambda x, minV, maxV: minV if x < minV else maxV if x > maxV else x if self.minimumZoomScale < 0.1: self.minimumZoomScale = 0.1 if self.maximumZoomScale < self.minimumZoomScale: self.maximumZoomScale = self.minimumZoomScale self.__zoomScale = clamp(self.__zoomScale, self.minimumZoomScale, self.maximumZoomScale) bounds = self.contentBounds() scaleX = bounds.width / self.__zoomScale scaleY = bounds.height / self.__zoomScale maxX = max(self.__contentSize.width - scaleX, 0) maxY = max(self.__contentSize.height - scaleY, 0) self.__contentOffset.x = clamp(self.__contentOffset.x, 0, maxX) self.__contentOffset.y = clamp(self.__contentOffset.y, 0, maxY) scale = core.LinearTransform2() scale.scale(self.__zoomScale) transform = core.AffineTransform2() transform.translate(-self.__contentOffset.x, -self.__contentOffset.y) transform.multiply(scale) transform.translate(bounds.x, bounds.y) tm = transform.matrix3() if self.contentTransform != tm: self.contentTransform = tm self._updateScrollSliderRect() self.redraw() self.__updateContentTransform = False def onClickZoomButton(self): print('user click zoom-button') if self.__zoomScale != 1.0: self.__zoomScale = 1.0 else: self.__zoomScale = self.minimumZoomScale self.updateContentTransform() def setContentOffset(self, x, y): if self.__contentOffset.x != x or self.__contentOffset.y != y: self.__contentOffset.tuple = x, y self.updateContentTransform() def onMouseDown(self, deviceId, buttonId, pos): super().onMouseDown(deviceId, buttonId, pos) if not self.__mouseTrackInfo and not self.isMouseCapturedBySelf( deviceId): bounds = self.contentBounds() pos = self.projectLocalPoint(pos) if not bounds.isInside(pos): mouseId = (deviceId, buttonId) if self.__verticalScrollSliderRect: if self.__verticalScrollSliderRect.isInside(pos): self.captureMouse(deviceId) offset = pos - self.__verticalScrollSliderRect.origin self.__mouseTrackInfo = _MouseTrackPos( mouseId, _POS_VERTICAL_SCROLL_SLIDER, offset) if self.__mouseHoverPos in ( _POS_VERTICAL_SCROLL_SLIDER, _POS_VERTICAL_SCROLL_TRACK): self.__mouseHoverPos = 0 # release mouse hover! self.redraw() return if self.__verticalScrollTrackRect.isInside(pos): range = self.__verticalScrollTrackRect.height - self.__verticalScrollSliderRect.height if range > 0: offset = pos.y - self.__verticalScrollTrackRect.y - self.__verticalScrollSliderRect.height * 0.5 maxValue = max( self.__contentSize.height - bounds.height / self.__zoomScale, 0) offset = 0.0 if offset < 0.0 else maxValue if offset > range else offset * maxValue / range self.screen().postOperation( self.setContentOffset, (self.__contentOffset.x, offset)) return if self.__horizontalScrollSliderRect: if self.__horizontalScrollSliderRect.isInside(pos): self.captureMouse(deviceId) offset = pos - self.__horizontalScrollSliderRect.origin self.__mouseTrackInfo = _MouseTrackPos( mouseId, _POS_HORIZONTAL_SCROLL_SLIDER, offset) if self.__mouseHoverPos in ( _POS_HORIZONTAL_SCROLL_SLIDER, _POS_HORIZONTAL_SCROLL_TRACK): self.__mouseHoverPos = 0 # release mouse hover self.redraw() return if self.__horizontalScrollTrackRect.isInside(pos): range = self.__horizontalScrollTrackRect.width - self.__horizontalScrollSliderRect.width if range > 0: offset = pos.x - self.__horizontalScrollTrackRect.x - self.__horizontalScrollSliderRect.width * 0.5 maxValue = max( self.__contentSize.width - bounds.width / self.__zoomScale, 0) offset = 0.0 if offset < 0.0 else maxValue if offset > range else offset * maxValue / range self.screen().postOperation( self.setContentOffset, (offset, self.__contentOffset.y)) return if self.showZoomButton and self.__zoomButtonRect: if self.__zoomButtonRect.isInside(pos): self.captureMouse(deviceId) offset = pos - self.__zoomButtonRect.origin self.__mouseTrackInfo = _MouseTrackPos( mouseId, _POS_ZOOM_BUTTON, offset) if self.__mouseHoverPos == _POS_ZOOM_BUTTON: self.__mouseHoverPos = 0 self.redraw() return def onMouseMove(self, deviceId, pos, delta): super().onMouseMove(deviceId, pos, delta) if self.__mouseTrackInfo and self.__mouseTrackInfo.mouseId[ 0] == deviceId: if self.isMouseCapturedBySelf(deviceId): bounds = self.contentBounds() pos = self.projectLocalPoint(pos) if self.__mouseTrackInfo.type == _POS_ZOOM_BUTTON: pos_old = core.Point(self.__mouseTrackInfo.offset + self.__zoomButtonRect.origin) act1 = self.__zoomButtonRect.isInside(pos_old) act2 = self.__zoomButtonRect.isInside(pos) self.__mouseTrackInfo.offset.tuple = ( pos - self.__zoomButtonRect.origin).tuple if act1 != act2: # zoom-button activation state changed. self.redraw() elif self.__mouseTrackInfo.type == _POS_VERTICAL_SCROLL_SLIDER: if self.__verticalScrollSliderRect: range = self.__verticalScrollTrackRect.height - self.__verticalScrollSliderRect.height if range > 0: offset = pos.y - self.__mouseTrackInfo.offset.y - self.__verticalScrollTrackRect.y maxValue = max( self.__contentSize.height - bounds.height / self.__zoomScale, 0) offset = 0.0 if offset < 0.0 else maxValue if offset > range else offset * maxValue / range self.screen().postOperation( self.setContentOffset, (self.__contentOffset.x, offset)) elif self.__mouseTrackInfo.type == _POS_HORIZONTAL_SCROLL_SLIDER: if self.__horizontalScrollSliderRect: range = self.__horizontalScrollTrackRect.width - self.__horizontalScrollSliderRect.width if range > 0: offset = pos.x - self.__mouseTrackInfo.offset.x - self.__horizontalScrollTrackRect.x maxValue = max( self.__contentSize.width - bounds.width / self.__zoomScale, 0) offset = 0.0 if offset < 0.0 else maxValue if offset > range else offset * maxValue / range self.screen().postOperation( self.setContentOffset, (offset, self.__contentOffset.y)) else: self.__mouseTrackInfo = None self.redraw() elif deviceId == 0: hoverPos = 0 if not self.isMouseCapturedBySelf(deviceId): bounds = self.contentBounds() pos = self.projectLocalPoint(pos) if not bounds.isInside(pos): trackPos = self.__mouseTrackInfo.type if self.__mouseTrackInfo else 0 if hoverPos == 0 and trackPos not in ( _POS_VERTICAL_SCROLL_SLIDER, _POS_VERTICAL_SCROLL_TRACK): # check mouse hover on vertical scroll if self.__verticalScrollSliderRect: if self.__verticalScrollSliderRect.isInside(pos): hoverPos = _POS_VERTICAL_SCROLL_SLIDER elif self.__verticalScrollTrackRect.isInside(pos): hoverPos = _POS_VERTICAL_SCROLL_TRACK if hoverPos == 0 and trackPos not in ( _POS_HORIZONTAL_SCROLL_SLIDER, _POS_HORIZONTAL_SCROLL_TRACK): # check mouse hover on horizontal scroll if self.__horizontalScrollSliderRect: if self.__horizontalScrollSliderRect.isInside(pos): hoverPos = _POS_HORIZONTAL_SCROLL_SLIDER elif self.__horizontalScrollTrackRect.isInside( pos): hoverPos = _POS_HORIZONTAL_SCROLL_TRACK if hoverPos == 0 and trackPos != _POS_ZOOM_BUTTON: if self.showZoomButton and self.__zoomButtonRect: if self.__zoomButtonRect.isInside(pos): hoverPos = _POS_ZOOM_BUTTON if self.__mouseHoverPos != hoverPos: self.__mouseHoverPos = hoverPos self.redraw() def onMouseUp(self, deviceId, buttonId, pos): super().onMouseUp(deviceId, buttonId, pos) if self.__mouseTrackInfo and self.__mouseTrackInfo.mouseId == ( deviceId, buttonId): bounds = self.contentBounds() pos = self.projectLocalPoint(pos) if self.__mouseTrackInfo.type == _POS_ZOOM_BUTTON: if self.__zoomButtonRect and self.__zoomButtonRect.isInside( pos): self.screen().postOperation(self.onClickZoomButton, ()) if deviceId == 0 and self.__mouseHoverPos == 0: self.__mouseHoverPos = self.__mouseTrackInfo.type self.releaseMouse(deviceId) self.__mouseTrackInfo = None self.redraw() def onMouseLeave(self, deviceId): super().onMouseLeave(deviceId) if deviceId == 0 and self.__mouseHoverPos: self.__mouseHoverPos = 0 if not self.__mouseTrackInfo: self.redraw() def onMouseWheel(self, deviceId, pos, delta): super().onMouseWheel(deviceId, pos, delta) window = self.screen().window from .. import vkey if window.isKeyDown(0, vkey.LEFT_OPTION): offset = 0.05 if delta.y > 0.0 else -0.05 zoomScale = self.__zoomScale + offset self.__zoomScale = max(zoomScale, 0.1) else: # OS X does handles vertical scroll while holding down shift-key. # if window.isKeyDown(0, vkey.LEFT_SHIFT): # self.__contentOffset.x -= delta.y * 10 # self.__contentOffset.y += delta.x * 10 # else: self.__contentOffset.x -= delta.x * 10 self.__contentOffset.y += delta.y * 10 self.updateContentTransform() def preprocessMouseEvent(self, type, deviceId, buttonId, pos, delta): if type == window.MOUSE_EVENT_MOVE and self.__mouseHoverPos: if self.screen().focusFrame(deviceId) is not None: bounds = self.contentBounds() pos = self.projectLocalPoint(pos) if bounds.isInside(pos): self.__mouseHoverPos = 0 self.redraw() else: self.__mouseHoverPos = 0 self.redraw() # 마우스가 자식 위로 올라가면.. 호버 없애야 함. return super().preprocessMouseEvent(type, deviceId, buttonId, pos, delta)
class RoundedBarProgressView(AnimatedProgressView): borderWidth = 1 borderColor = core.Color(0.0, 0.0, 0.0) backgroundColor = core.Color(0.5, 0.5, 0.5) progressColor = core.Color(1.0, 1.0, 1.0) minimumViewHeight = borderWidth * 2 + 1 minimumViewWidth = borderWidth * 2 + 1 cornerRadius = 8 vertical = False def __init__(self, initialValue=0.0, minValue=0.0, maxValue=1.0, *args, **kwargs): super().__init__(initialValue, minValue, maxValue, *args, **kwargs) self.setBlendState(blendstate.defaultAlpha) self.__bsLayerMask1 = None self.__bsLayerMask2 = None def discardSurface(self): super().discardSurface() self.__bsLayerMask1 = None self.__bsLayerMask2 = None def onResized(self): super().onResized() self.__bsLayerMask1 = None self.__bsLayerMask2 = None def onLoaded(self): super().onLoaded() self.__bsLayerMask1 = None self.__bsLayerMask2 = None def onUnload(self): super().onUnload() self.__bsLayerMask1 = None self.__bsLayerMask2 = None def onRender(self, renderer): border = round(self.borderWidth) border2 = border * 2 radius = round(self.cornerRadius) bounds = self.bounds() cornerRadius = min(radius, math.floor(bounds.height * 0.5) - border, math.floor(bounds.width * 0.5) - border) if cornerRadius > 1: texSize = (cornerRadius + border) * 2 cornerRadius2 = cornerRadius * 2 if self.__bsLayerMask1 is None: rt = rendertarget.RenderTarget(texSize * self.scaleFactor, texSize * self.scaleFactor, rendertarget.DEPTH_NONE) rd = Renderer(rt) rd.bounds = 0, 0, texSize, texSize rd.clear(core.Color(0, 0, 0, 0)) with rd.contextForSolidEllipses( core.Color(1, 1, 1, 1), blend=blendstate.defaultOpaque) as r: r.add( core.Rect(border, border, cornerRadius2, cornerRadius2)) self.__bsLayerMask1 = rt.colorTextureAtIndex(0) if border > 0: if self.__bsLayerMask2 is None: rt = rendertarget.RenderTarget(texSize * self.scaleFactor, texSize * self.scaleFactor, rendertarget.DEPTH_NONE) rd = Renderer(rt) rd.bounds = 0, 0, texSize, texSize rd.clear(core.Color(0, 0, 0, 0)) with rd.contextForSolidEllipses( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add(core.Rect(0, 0, texSize, texSize)) with rd.contextForSolidEllipses( core.Color(0, 0, 0, 0), blend=blendstate.defaultOpaque) as r: r.add( core.Rect(border, border, cornerRadius2, cornerRadius2)) self.__bsLayerMask2 = rt.colorTextureAtIndex(0) else: self.__bsLayerMask2 = None else: self.__bsLayerMask1 = None self.__bsLayerMask2 = None rc = core.Rect(bounds.x + border, bounds.y + border, bounds.width - border2, bounds.height - border2) prog = self._progress / (self.maximumValue - self.minimumValue) if prog < 0.0: prog = 0.0 if prog > 1.0: prog = 1.0 if self.vertical: rc.height = rc.height * prog else: rc.width = rc.width * prog if border > 0: renderer.clear(self.borderColor) with renderer.contextForSolidRects( self.progressColor, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.vertical: rc.y += rc.height rc.height = (bounds.height - border2) - rc.height else: rc.x += rc.width rc.width = (bounds.width - border2) - rc.width with renderer.contextForSolidRects( self.backgroundColor, blend=blendstate.defaultOpaque) as r: r.add(rc) else: renderer.clear(self.backgroundColor) with renderer.contextForSolidRects( self.progressColor, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.__bsLayerMask1: invScale = 1.0 / self.scaleFactor x = self.__bsLayerMask1.width * 0.5 * invScale y = self.__bsLayerMask1.height * 0.5 * invScale with renderer.contextForTexturedRects( self.__bsLayerMask1, blend=blendstate.defaultDarken) as r: r.add(core.Rect(bounds.x, bounds.y, x, y), _identityMatrix3, core.Rect(0, 0, 0.5, 0.5)) r.add(core.Rect(bounds.x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0, 0.5, 0.5, 0.5)) r.add( core.Rect(bounds.x + bounds.width - x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0.5, 0.5, 0.5, 0.5)) r.add(core.Rect(bounds.x + bounds.width - x, bounds.y, x, y), _identityMatrix3, core.Rect(0.5, 0.0, 0.5, 0.5)) if self.__bsLayerMask2: x = self.__bsLayerMask2.width * 0.5 * invScale y = self.__bsLayerMask2.height * 0.5 * invScale with renderer.contextForTexturedRects( self.__bsLayerMask2, blend=blendstate.defaultAlpha) as r: r.add(core.Rect(bounds.x, bounds.y, x, y), _identityMatrix3, core.Rect(0, 0, 0.5, 0.5)) r.add( core.Rect(bounds.x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0, 0.5, 0.5, 0.5)) r.add( core.Rect(bounds.x + bounds.width - x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0.5, 0.5, 0.5, 0.5)) r.add( core.Rect(bounds.x + bounds.width - x, bounds.y, x, y), _identityMatrix3, core.Rect(0.5, 0.0, 0.5, 0.5))
class Menu(view.View): minimumViewHeight = 16 minimumViewWidth = 16 fontAttributes = font.attributes(12) backgroundColor = core.Color(0.94, 0.94, 0.94) backgroundColorHighlighted = core.Color(0.2, 0.2, 1) borderColor = core.Color(0, 0, 0) borderWidth = 1 margin = 2 # left,right,bottom,up margin itemPadding = 1 # padding between menu items verticalLayout = False def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.style = ItemStyle() self.__items = [] self.__highlightedItem = None self.__selectedItem = None self.__popup = False self.__popupChild = None self.__capturedMouseId = None self.__capturedMouseDown = False self.__autoExpand = False def items(self): return tuple(self.__items) def findItem(self, text): for item in self.__items: try: if item.text == text: return item except AttributeError: pass def removeItem(self, item): if item.menu != self: raise ValueError('Invalid menu item object.') self.__items.remove(item) item.style = None item._menu = None self.__highlightedItem = None self.redraw() def addItem(self, text): item = Item(text, menu=self, style=self.style) self.__items.append(item) self.__highlightedItem = None self.redraw() return item def addSeparator(self): item = Separator(menu=self, style=self.style) self.__items.append(item) self.__highlightedItem = None self.redraw() return item def popupOnScreen(self, screen, origin, parentMenu=None): if self.parent() is None: self.unload() self.__popup = True self.__autoExpand = True self.verticalLayout = True root = screen.frame root.addChild(self) self.load(screen) # load font to calculate frame size = self.calculateFrameSize() sizeInPixel = self.convertLocalToPixel(size) sizeInRoot = root.convertPixelToLocal(sizeInPixel) origin.y -= sizeInRoot.height originInPixel = root.convertLocalToPixel(origin) originInPixel.x = math.floor(originInPixel.x + 0.5) originInPixel.y = math.floor(originInPixel.y + 0.5) originInRoot = root.convertPixelToLocal(originInPixel) frame = core.Rect(originInRoot, sizeInRoot) bounds = root.contentDisplayBounds() if frame.x < bounds.x: frame.x = bounds.x if frame.y < bounds.y: frame.y = bounds.y if frame.x + frame.width > bounds.x + bounds.width: frame.x = bounds.x + bounds.width - frame.width if frame.y + frame.height > bounds.y + bounds.height: frame.y = bounds.y + bounds.height - frame.height self.frame = frame self.redraw() if not isinstance(parentMenu, Menu): screen.postOperation(self._popupMouseSetup, ()) return self def itemHitTest(self, pos): bounds = self.contentDisplayBounds() if bounds.isInside(pos): for item in self.__items: if item.hitTest(pos): return item def popupHitTest(self, pos): if self.parent(): if self.__popupChild: posInRoot = self.convertLocalToRoot(pos) posInLocal = self.__popupChild.convertRootToLocal(posInRoot) result = self.__popupChild.popupHitTest(posInLocal) if result: return result bounds = self.contentDisplayBounds() if bounds.isInside(pos): return self def _popupMouseSetup(self): if self.__capturedMouseId is None: self.captureMouse(0) self.__capturedMouseId = 0 self.__capturedMouseDown = False def onLoaded(self): super().onLoaded() self.style.font = self.font def onUnload(self): self.style.font = None self.__highlightedItem = None self.__selectedItem = None super().onUnload() def _calculatePopupFrameFromItem(self, item): parent = item.menu root = parent.screen().frame rect = item.displayRect bounds = root.contentDisplayBounds() size = self.calculateFrameSize() sizeInPixel = self.convertLocalToPixel(size) sizeInRoot = root.convertPixelToLocal(sizeInPixel) if parent.verticalLayout: left = parent.convertLocalToRoot( core.Point(rect.x, rect.y + rect.height)) left.x -= sizeInRoot.width right = parent.convertLocalToRoot( core.Point(rect.x + rect.width, rect.y + rect.height)) if right.x + sizeInRoot.width <= bounds.x + bounds.width: origin = right elif left.x >= bounds.x: origin = left else: d1 = (right.x + sizeInRoot.width) - (bounds.x + bounds.width) d2 = bounds.x - left.x origin = left if d1 > d2 else right origin.y -= sizeInRoot.height else: top = parent.convertLocalToRoot( core.Point(rect.x, rect.y + rect.height)) bottom = parent.convertLocalToRoot(core.Point(rect.x, rect.y)) bottom.y -= sizeInRoot.height if bottom.y >= bounds.y: origin = bottom elif top.y + size.height <= bounds.y + bounds.height: origin = top else: d1 = bounds.y - bottom.y d2 = (top.y + size.height) - (bounds.y + bounds.height) origin = top if d1 > d2 else bottom originInPixel = root.convertLocalToPixel(origin) originInPixel.x = math.floor(originInPixel.x + 0.5) originInPixel.y = math.floor(originInPixel.y + 0.5) origin = root.convertPixelToLocal(originInPixel) frame = core.Rect(origin, sizeInRoot) # frame.x = math.floor(frame.x * self.scaleFactor + 0.5) / self.scaleFactor # frame.y = math.floor(frame.y * self.scaleFactor + 0.5) / self.scaleFactor if frame.x < bounds.x: frame.x = bounds.x if frame.y < bounds.y: frame.y = bounds.y if frame.x + frame.width > bounds.x + bounds.width: frame.x = bounds.x + bounds.width - frame.width if frame.y + frame.height > bounds.y + bounds.height: frame.y = bounds.y + bounds.height - frame.height return frame def calculateFrameSize(self): style = self.style padding = self.itemPadding scale = 1.0 / self.scaleFactor numItems = len(self.__items) itemPadding = padding * (numItems - 1) if numItems > 0 else 0 width = style.minimumWidth height = style.minimumHeight if self.__popup or self.verticalLayout: height = 0 for item in self.__items: w, h = item.calculateFrameSize(_LAYOUT_VERTICAL, scale) width = max(width, w) height += h height = max(height + itemPadding, style.minimumHeight) else: width = 0 for item in self.items(): w, h = item.calculateFrameSize(_LAYOUT_HORIZONTAL, scale) width += w height = max(height, h) width = max(width + itemPadding, style.minimumWidth) width += self.margin * 2 height += self.margin * 2 return core.Size(width + self.borderWidth * 2, height + self.borderWidth * 2) def getResolution(self): if self.isPopup(): s = self.calculateFrameSize() self.contentScale = s return s.width * self.scaleFactor, s.height * self.scaleFactor return super().getResolution() def onRender(self, renderer): super().onRender(renderer) bounds = self.contentBounds() itemPadding = self.itemPadding offsetX = bounds.x + self.margin width = bounds.width - self.margin * 2 height = bounds.height - self.margin * 2 scale = 1.0 / self.scaleFactor state = _ITEM_STATE_NORMAL if self.enabled else _ITEM_STATE_DISABLED if self.__popup or self.verticalLayout: offsetY = bounds.y + bounds.height - self.margin for item in self.__items: w, h = item.calculateFrameSize(_LAYOUT_VERTICAL, scale) offsetY -= h rc = core.Rect(offsetX, offsetY, max(w, width), h) itemState = state if self.__highlightedItem == item and item.selectable( ) and itemState == _ITEM_STATE_NORMAL: itemState = _ITEM_STATE_HIGHLIGHTED with renderer.contextForSolidRects( self.backgroundColorHighlighted) as r: r.add(rc) item.drawRect(renderer, rc, itemState, _LAYOUT_VERTICAL) offsetY -= itemPadding else: offsetY = bounds.y + self.margin for item in self.__items: w, h = item.calculateFrameSize(_LAYOUT_HORIZONTAL, scale) rc = core.Rect(offsetX, offsetY, w, max(h, height)) itemState = state if self.__highlightedItem == item and item.selectable( ) and itemState == _ITEM_STATE_NORMAL: itemState = _ITEM_STATE_HIGHLIGHTED with renderer.contextForSolidRects( self.backgroundColorHighlighted) as r: r.add(rc) item.drawRect(renderer, rc, itemState, _LAYOUT_HORIZONTAL) offsetX = offsetX + w + itemPadding def onUpdate(self, delta, tick, date): super().onUpdate(delta, tick, date) def updateLayout(self): if self.__popup: frame = self.frame frame.size = self.calculateFrameSize() self.frame = frame self.redraw() def isPopup(self): return self.__popup def dismiss(self): if self.__popupChild: self.__popupChild.dismiss() self.__popupChild = None self.__capturedMouseId = None self.__capturedMouseDown = False self.releaseAllMiceCapturedBySelf() if self.isPopup(): self.removeFromParent(unload=True) else: self.__highlightedItem = None self.__autoExpand = False self.redraw() def setItemActivate(self, item): if not isinstance(item, Item) or not item.selectable(): item = None if self.__highlightedItem != item: self.__highlightedItem = item self.redraw() if item and self.__autoExpand: menu = item.subMenu if menu: if self.__popupChild != menu: if self.__popupChild: self.screen().postOperation(self.__popupChild.dismiss, ()) self.__popupChild = menu.popupOnScreen( self.screen(), core.Point(0, 0), self) self.__popupChild.frame = self.__popupChild._calculatePopupFrameFromItem( item) else: if self.__popupChild: self.screen().postOperation(self.__popupChild.dismiss, ()) self.__popupChild = None elif self.__popupChild: self.screen().postOperation(self.__popupChild.dismiss, ()) self.__popupChild = None def onMouseLeave(self, deviceId): super().onMouseLeave(deviceId) if deviceId == 0 and not self.isMouseCapturedBySelf(0): if self.__popupChild is None: if self.__highlightedItem: self.__highlightedItem = None self.redraw() def onMouseHover(self, deviceId): super().onMouseHover(deviceId) def onMouseDown(self, deviceId, buttonId, pos): super().onMouseDown(deviceId, buttonId, pos) if buttonId == 0: if not self.isPopup(): item = self.itemHitTest(pos) if item and item.selectable(): if self.__capturedMouseId is None: self.captureMouse(deviceId) self.__capturedMouseId = deviceId self.__capturedMouseDown = True self.__autoExpand = True else: if self.__capturedMouseId == deviceId: self.__autoExpand = not self.__autoExpand elif not self.__capturedMouseDown: self.captureMouse(deviceId) self.__capturedMouseId = deviceId self.__capturedMouseDown = True self.__autoExpand = True item = None menu = self.popupHitTest(pos) if menu: if self.__capturedMouseId == deviceId and buttonId == 0: posInRoot = self.convertLocalToRoot(pos) posInLocal = menu.convertRootToLocal(posInRoot) item = menu.itemHitTest(posInLocal) if item: menu.setItemActivate(item) else: self.screen().postOperation(self.dismiss, ()) def onMouseUp(self, deviceId, buttonId, pos): super().onMouseUp(deviceId, buttonId, pos) if self.__capturedMouseId == deviceId and buttonId == 0: if self.isMouseCapturedBySelf(deviceId): menu = self.popupHitTest(pos) if menu: posInRoot = self.convertLocalToRoot(pos) posInLocal = menu.convertRootToLocal(posInRoot) item = menu.itemHitTest(posInLocal) if item and item.selectable(): if item.subMenu is None: self.screen().postOperation(self.dismiss, ()) if item.callback: self.screen().postOperation( item.callback, (item, )) self.__capturedMouseDown = False if not self.isPopup() and not self.__autoExpand: hItem = self.__highlightedItem self.dismiss() self.__highlightedItem = hItem else: # mouse lost? self.screen().postOperation(self.dismiss, ()) def onMouseMove(self, deviceId, pos, delta): super().onMouseMove(deviceId, pos, delta) updateItem = False if self.__capturedMouseId == deviceId: if self.isMouseCapturedBySelf(deviceId): updateItem = True else: self.__capturedMouseId = None # mouse lost? elif deviceId == 0 and self.__capturedMouseId is None: updateItem = True if updateItem: menu = self.popupHitTest(pos) if menu: posInRoot = self.convertLocalToRoot(pos) posInLocal = menu.convertRootToLocal(posInRoot) item = menu.itemHitTest(posInLocal) if item: menu.setItemActivate(item) elif not self.__popupChild: self.setItemActivate(None) def onMouseLost(self, deviceId): super().onMouseLost(deviceId) self.screen().postOperation(self.dismiss, ())
def onRender(self, renderer): border = round(self.borderWidth) border2 = border * 2 radius = round(self.cornerRadius) bounds = self.bounds() cornerRadius = min(radius, math.floor(bounds.height * 0.5) - border, math.floor(bounds.width * 0.5) - border) if cornerRadius > 1: texSize = (cornerRadius + border) * 2 cornerRadius2 = cornerRadius * 2 if self.__bsLayerMask1 is None: rt = rendertarget.RenderTarget(texSize * self.scaleFactor, texSize * self.scaleFactor, rendertarget.DEPTH_NONE) rd = Renderer(rt) rd.bounds = 0, 0, texSize, texSize rd.clear(core.Color(0, 0, 0, 0)) with rd.contextForSolidEllipses( core.Color(1, 1, 1, 1), blend=blendstate.defaultOpaque) as r: r.add( core.Rect(border, border, cornerRadius2, cornerRadius2)) self.__bsLayerMask1 = rt.colorTextureAtIndex(0) if border > 0: if self.__bsLayerMask2 is None: rt = rendertarget.RenderTarget(texSize * self.scaleFactor, texSize * self.scaleFactor, rendertarget.DEPTH_NONE) rd = Renderer(rt) rd.bounds = 0, 0, texSize, texSize rd.clear(core.Color(0, 0, 0, 0)) with rd.contextForSolidEllipses( self.borderColor, blend=blendstate.defaultOpaque) as r: r.add(core.Rect(0, 0, texSize, texSize)) with rd.contextForSolidEllipses( core.Color(0, 0, 0, 0), blend=blendstate.defaultOpaque) as r: r.add( core.Rect(border, border, cornerRadius2, cornerRadius2)) self.__bsLayerMask2 = rt.colorTextureAtIndex(0) else: self.__bsLayerMask2 = None else: self.__bsLayerMask1 = None self.__bsLayerMask2 = None rc = core.Rect(bounds.x + border, bounds.y + border, bounds.width - border2, bounds.height - border2) prog = self._progress / (self.maximumValue - self.minimumValue) if prog < 0.0: prog = 0.0 if prog > 1.0: prog = 1.0 if self.vertical: rc.height = rc.height * prog else: rc.width = rc.width * prog if border > 0: renderer.clear(self.borderColor) with renderer.contextForSolidRects( self.progressColor, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.vertical: rc.y += rc.height rc.height = (bounds.height - border2) - rc.height else: rc.x += rc.width rc.width = (bounds.width - border2) - rc.width with renderer.contextForSolidRects( self.backgroundColor, blend=blendstate.defaultOpaque) as r: r.add(rc) else: renderer.clear(self.backgroundColor) with renderer.contextForSolidRects( self.progressColor, blend=blendstate.defaultOpaque) as r: r.add(rc) if self.__bsLayerMask1: invScale = 1.0 / self.scaleFactor x = self.__bsLayerMask1.width * 0.5 * invScale y = self.__bsLayerMask1.height * 0.5 * invScale with renderer.contextForTexturedRects( self.__bsLayerMask1, blend=blendstate.defaultDarken) as r: r.add(core.Rect(bounds.x, bounds.y, x, y), _identityMatrix3, core.Rect(0, 0, 0.5, 0.5)) r.add(core.Rect(bounds.x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0, 0.5, 0.5, 0.5)) r.add( core.Rect(bounds.x + bounds.width - x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0.5, 0.5, 0.5, 0.5)) r.add(core.Rect(bounds.x + bounds.width - x, bounds.y, x, y), _identityMatrix3, core.Rect(0.5, 0.0, 0.5, 0.5)) if self.__bsLayerMask2: x = self.__bsLayerMask2.width * 0.5 * invScale y = self.__bsLayerMask2.height * 0.5 * invScale with renderer.contextForTexturedRects( self.__bsLayerMask2, blend=blendstate.defaultAlpha) as r: r.add(core.Rect(bounds.x, bounds.y, x, y), _identityMatrix3, core.Rect(0, 0, 0.5, 0.5)) r.add( core.Rect(bounds.x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0, 0.5, 0.5, 0.5)) r.add( core.Rect(bounds.x + bounds.width - x, bounds.y + bounds.height - y, x, y), _identityMatrix3, core.Rect(0.5, 0.5, 0.5, 0.5)) r.add( core.Rect(bounds.x + bounds.width - x, bounds.y, x, y), _identityMatrix3, core.Rect(0.5, 0.0, 0.5, 0.5))
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setAmbientColor(core.Color(1, 1, 1, 1)) self.drawMode = DRAW_MESHES self.lights = []
class Item(_ItemContext): """ Menu Item. if item has subMenu, item's callback will not invoked. """ textColor = core.Color(0, 0, 0) outlineColor = None def __init__(self, text, *args, **kwargs): super().__init__(*args, **kwargs) self.callback = None self.subMenu = None self.__checked = False self.__text = text self.__enabled = True self.__image = None @property def enabled(self): return self.__enabled @enabled.setter def enabled(self, value): v = bool(value) if self.__enabled != v: self.__enabled = v menu = self.menu if menu: menu.redraw() @property def text(self): return self.__text @text.setter def text(self, value): text = str(value) if self.__text != text: self.__text = text menu = self.menu if menu: menu.updateLayout() @property def image(self): return self.__image @image.setter def image(self, value): self.__image = value menu = self.menu if menu: menu.updateLayout() @property def checked(self): return self.__checked @checked.setter def checked(self, value): self.__checked = bool(value) menu = self.menu if menu: menu.redraw() def selectable(self): ''' determine whether item is selectable or not. items must be enabled, has subMenu or callable to be selectable. ''' if self.enabled: if self.subMenu or self.callback: return True return super().selectable() def calculateFrameSize(self, layout, scale): style = self.style if style.font: h = style.font.textFont.lineHeight() * scale w = style.font.textFont.lineWidth(self.__text) * scale h += style.topMargin + style.bottomMargin w += style.checkmarkSize + style.padding + style.textMargin + style.arrowSize + style.leftMargin + style.rightMargin if self.__image: w += style.imageSize + style.padding return math.ceil(w), math.ceil(h) return self.style.minimumWidth, self.style.minimumHeight def drawRect(self, renderer, rect, state, layout): super().drawRect(renderer, rect, state, layout) style = self.style if style.font: if not self.selectable() or state == _ITEM_STATE_DISABLED: textColor = style.textColorDisabled outlineColor = style.outlineColorDisabled checkmarkColor = style.checkmarkColorDisabled arrowColor = style.arrowColorDisabled elif state == _ITEM_STATE_HIGHLIGHTED: textColor = style.textColorHighlighted outlineColor = style.outlineColorHighlighted checkmarkColor = style.checkmarkColorHighlighted arrowColor = style.arrowColorHighlighted else: textColor = style.textColor outlineColor = style.outlineColor checkmarkColor = style.checkmarkColor arrowColor = style.arrowColor rc = core.Rect(rect) rc.x += style.leftMargin rc.y += style.bottomMargin rc.width -= style.textMargin + style.arrowSize + style.leftMargin + style.rightMargin rc.height -= style.topMargin + style.bottomMargin if self.__checked: # draw check mark y = (rc.height - style.checkmarkSize) * 0.5 cbRect = core.Rect(rc.x, rc.y + y, style.checkmarkSize, style.checkmarkSize) p0 = core.Point(0.15, 0.6) p1 = core.Point(0.05, 0.5) p2 = core.Point(0.4, 0.35) p3 = core.Point(0.4, 0.15) p4 = core.Point(0.85, 0.8) p5 = core.Point(0.95, 0.7) for p in (p0, p1, p2, p3, p4, p5): p.x = p.x * cbRect.width + cbRect.x p.y = p.y * cbRect.height + cbRect.y with renderer.contextForSolidTriangleStrip( checkmarkColor, blend=blendstate.defaultOpaque) as r: r.addTriangle(p0, p1, p2) r.add(p3) r.add(p4) r.add(p5) offset = style.checkmarkSize + style.padding rc.x += offset rc.width -= offset if self.__image: y = (rc.height - style.imageSize) * 0.5 rc2 = core.Rect(rc.x, rc.y + y, style.imageSize, style.imageSize) with renderer.contextForTexturedRects( self.__image, blend=blendstate.defaultAlpha) as r: r.add(rc2, core.Matrix3()) offset = style.imageSize + style.padding rc.x += offset rc.width -= offset font.drawText(renderer, rc, self.__text, style.font, textColor, outlineColor, align=font.ALIGN_BOTTOM_LEFT, linebreak=font.LINE_BREAK_TRUNCATING_TAIL) #draw arrow! if self.subMenu: if layout == _LAYOUT_HORIZONTAL: arRect = core.Rect( rect.x + rect.width - style.arrowSize - style.rightMargin, rect.y + style.bottomMargin, style.arrowSize, style.arrowSize) x1 = arRect.x x2 = arRect.x + arRect.width * 0.5 x3 = arRect.x + arRect.width y1 = arRect.y y2 = arRect.y + arRect.height * 0.5 pos = (core.Point(x1, y2), core.Point(x2, y1), core.Point(x3, y2)) else: y = (rc.height - style.arrowSize) * 0.5 arRect = core.Rect( rect.x + rect.width - style.arrowSize - style.rightMargin, rc.y + y, style.arrowSize, style.arrowSize) # x1 = arRect.x + arRect.width * 0.5 # x2 = arRect.x + arRect.width w = arRect.height * 0.8660254037844388 x1 = arRect.x + (arRect.width - w) * 0.5 x2 = x1 + w y1 = arRect.y y2 = arRect.y + arRect.height * 0.5 y3 = arRect.y + arRect.height pos = (core.Point(x1, y3), core.Point(x1, y1), core.Point(x2, y2)) if arrowColor: with renderer.contextForSolidTriangles( arrowColor, blend=blendstate.defaultOpaque) as r: r.add(*pos) else: print('drawRect textFont is missing!!')