def test_register_ui_order(self): class LookAndFeelFixture(LookAndFeel): @property def default_ui(self) -> ComponentUI[Component]: return GlassComponentUI() laf = LookAndFeelFixture(self.context.toolkit) laf.register_ui(LabelButton, GlassLabelButtonUI) laf.register_ui(Button, GlassButtonUI) laf.register_ui(Label, GlassLabelUI) self.assertTrue( isinstance(laf.create_ui(LabelButton(self.context)), GlassLabelButtonUI)) self.assertTrue( isinstance(laf.create_ui(Button(self.context)), GlassButtonUI)) self.assertTrue( isinstance(laf.create_ui(Label(self.context)), GlassLabelUI)) laf = LookAndFeelFixture(self.context.toolkit) laf.register_ui(Button, GlassButtonUI) laf.register_ui(Label, GlassLabelUI) laf.register_ui(LabelButton, GlassLabelButtonUI) self.assertTrue( isinstance(laf.create_ui(LabelButton(self.context)), GlassLabelButtonUI)) self.assertTrue( isinstance(laf.create_ui(Button(self.context)), GlassButtonUI)) self.assertTrue( isinstance(laf.create_ui(Label(self.context)), GlassLabelUI))
def test_style_fallback(self): label = Label(self.context) prefixes = list(label.style_fallback_prefixes) keys = list(label.style_fallback_keys(StyleKeys.Background)) self.assertEqual(["Label"], prefixes) self.assertEqual(["Label.background", "background"], keys)
def start(self, args: dict): from alleycat.ui.blender import UI self.context = UI().create_context() window = Frame(self.context, BorderLayout()) window.bounds = Bounds(160, 70, 280, 200) panel = Panel(self.context, HBoxLayout()) panel.set_color(StyleKeys.Background, RGBA(0.3, 0.3, 0.3, 0.8)) window.add(panel, padding=Insets(10, 10, 10, 10)) icon = Canvas(self.context, self.context.toolkit.images["cat.png"]) icon.minimum_size_override = Some(Dimension(64, 64)) panel.add(icon) label = Label(self.context, text_size=18) label.set_color(StyleKeys.Text, RGBA(1, 1, 1, 1)) panel.add(label) button1 = LabelButton(self.context, text_size=16, text="Button 1") button2 = LabelButton(self.context, text_size=16, text="Button 2") buttons = Panel(self.context, HBoxLayout(spacing=10, direction=BoxDirection.Reverse)) buttons.add(button2) buttons.add(button1) window.add(buttons, Border.Bottom, Insets(0, 10, 10, 10)) def handle_button(button: str): if len(button) > 0: label.text = f"{button} is pressed" panel.set_color(StyleKeys.Background, RGBA(1, 0, 0, 1)) else: label.text = "" panel.set_color(StyleKeys.Background, RGBA(0.1, 0.1, 0.1, 0.8)) button1_active = button1.observe("active").pipe( ops.map(lambda v: "Button 1" if v else "")) button2_active = button2.observe("active").pipe( ops.map(lambda v: "Button 2" if v else "")) button_active = rx.combine_latest(button1_active, button2_active).pipe( ops.map(lambda v: v[0] + v[1])) button_active.subscribe(handle_button, on_error=self.context.error_handler) window.draggable = True window.resizable = True
def test_ui_extents(self): label = Label(self.context) ui = cast(LabelUI, label.ui) self.assertEqual(Dimension(0, 0), ui.extents(label)) label.text = "Test" label.validate() self.assertAlmostEqual(20.02, ui.extents(label).width, delta=TextTolerance) self.assertAlmostEqual(7.227, ui.extents(label).height, delta=TextTolerance) label.text_size = 15 label.validate() self.assertAlmostEqual(30.03, ui.extents(label).width, delta=TextTolerance) self.assertAlmostEqual(10.840, ui.extents(label).height, delta=TextTolerance)
def draw_text(self, g: Graphics, component: Label, font: FontFace, color: RGBA) -> None: text = component.text size = component.text_size extents = component.context.toolkit.fonts.text_extent(text, font, size) padding = component.resolve_insets(StyleKeys.Padding).value_or( Insets(0, 0, 0, 0)) (x, y, w, h) = component.bounds.tuple rh = self._ratio_for_align[component.text_align] rv = self._ratio_for_align[component.text_vertical_align] tx = (w - extents.width - padding.left - padding.right) * rh + x + padding.left ty = (h - extents.height - padding.top - padding.bottom) * rv + extents.height + y + padding.top g.set_font_face(font) g.set_font_size(size) # FIXME: Temporary workaround until we get a better way of handling text shadows. if component.shadow: g.move_to(tx + 1, ty + 1) g.set_source_rgba(0, 0, 0, 0.8) g.show_text(text) g.move_to(tx, ty) g.set_source_rgba(color.r, color.g, color.b, color.a) g.show_text(text)
def test_align(self): window = Frame(self.context) window.bounds = Bounds(0, 0, 100, 100) label = Label(self.context) label.text = "AlleyCat" label.text_size = 18 label.bounds = Bounds(0, 0, 100, 100) window.add(label) self.context.process() self.assertImage("align_default", self.context, tolerance=Tolerance) for align in TextAlign: for vertical_align in TextAlign: label.text_align = align label.text_vertical_align = vertical_align test_name = f"align_{align}_{vertical_align}".replace( "TextAlign.", "") self.context.process() self.assertImage(test_name, self.context, tolerance=Tolerance)
def on_invalidate(self, component: Label) -> Observable: text_changes = component.observe("text") size_changes = component.observe("text_size") style_changes = self.on_style_change(component) font_changes = style_changes.pipe( ops.filter(lambda e: isinstance(e, FontChangeEvent)), ops.filter(lambda e: e.key in component.style_fallback_keys( StyleKeys.Text))) padding_changes = style_changes.pipe( ops.filter(lambda e: isinstance(e, InsetsChangeEvent)), ops.filter(lambda e: e.key in component.style_fallback_keys( StyleKeys.Padding))) return rx.merge(super().on_invalidate(component), text_changes, size_changes, font_changes, padding_changes)
def padding(self, component: Label) -> Insets: return component.resolve_insets(StyleKeys.Padding).value_or( Insets(0, 0, 0, 0))
def font(self, component: Label) -> FontFace: fonts = component.context.toolkit.fonts return component.resolve_font(StyleKeys.Text).value_or( fonts.fallback_font)
def text_color(self, component: Label) -> Maybe[RGBA]: return component.resolve_color(StyleKeys.Text)
def test_validation(self): laf = self.context.look_and_feel fonts = self.context.toolkit.fonts label = Label(self.context) label.validate() self.assertEqual(True, label.valid) label.text = "Label" self.assertEqual(False, label.valid) label.validate() label.text_size = 20 self.assertEqual(False, label.valid) label.validate() label.text_align = TextAlign.End label.text_vertical_align = TextAlign.End self.assertEqual(True, label.valid) def test_style(lookup: StyleLookup): label.validate() lookup.set_font("NonExistentKey", fonts["Font1"]) lookup.set_insets("NonExistentKey", Insets(10, 10, 10, 10)) self.assertEqual(True, label.valid) lookup.set_font(StyleKeys.Text, fonts["Font1"]) self.assertEqual(False, label.valid) label.validate() lookup.set_insets(StyleKeys.Padding, Insets(10, 10, 10, 10)) self.assertEqual(False, label.valid) test_style(laf) test_style(label)
def test_draw(self): window = Frame(self.context) window.bounds = Bounds(0, 0, 100, 60) label = Label(self.context) label.text = "Text" label.bounds = Bounds(0, 30, 60, 30) label2 = Label(self.context) label2.text = "AlleyCat" label2.text_size = 18 label2.set_color(StyleKeys.Text, RGBA(1, 0, 0, 1)) label2.bounds = Bounds(20, 0, 80, 60) window.add(label) window.add(label2) self.context.process() self.assertImage("draw", self.context, tolerance=Tolerance)
def test_with_padding(padding: Insets): with Label(self.context) as label: calculated = [] label.set_insets(StyleKeys.Padding, padding) label.validate() pw = padding.left + padding.right ph = padding.top + padding.bottom rv.observe(label.preferred_size).subscribe(calculated.append) self.assertEqual(Nothing, label.preferred_size_override) self.assertEqual(Dimension(pw, ph), label.preferred_size) self.assertEqual([Dimension(pw, ph)], calculated) label.text = "Test" label.validate() self.assertEqual(2, len(calculated)) self.assertEqual(Nothing, label.preferred_size_override) self.assertAlmostEqual(20.02 + pw, label.preferred_size.width, delta=TextTolerance) self.assertAlmostEqual(7.227 + ph, label.preferred_size.height, delta=TextTolerance) self.assertEqual(2, len(calculated)) self.assertAlmostEqual(20.02 + pw, calculated[1].width, delta=TextTolerance) self.assertAlmostEqual(7.227 + ph, calculated[1].height, delta=TextTolerance) label.text_size = 15 label.validate() self.assertEqual(3, len(calculated)) self.assertEqual(Nothing, label.preferred_size_override) self.assertAlmostEqual(30.03 + pw, label.preferred_size.width, delta=TextTolerance) self.assertAlmostEqual(10.840 + ph, label.preferred_size.height, delta=TextTolerance) self.assertEqual(3, len(calculated)) self.assertAlmostEqual(30.03 + pw, calculated[2].width, delta=TextTolerance) self.assertAlmostEqual(10.840 + ph, calculated[2].height, delta=TextTolerance) label.preferred_size_override = Some(Dimension(80, 50)) label.validate() self.assertEqual(Some(Dimension(80, 50)), label.preferred_size_override) self.assertEqual(Dimension(80, 50), label.preferred_size) self.assertEqual(4, len(calculated)) self.assertEqual(Dimension(80, 50), calculated[3]) label.preferred_size_override = Some(Dimension(10, 10)) label.validate() self.assertEqual(calculated[2], label.preferred_size) self.assertEqual(5, len(calculated)) self.assertEqual(calculated[2], calculated[4]) label.minimum_size_override = Some(Dimension(400, 360)) label.validate() self.assertEqual(Dimension(400, 360), label.preferred_size) self.assertEqual(6, len(calculated)) self.assertEqual(Dimension(400, 360), calculated[5])