Ejemplo n.º 1
0
    def minimum_size(self, component: Label) -> Dimension:
        (width, height) = self.extents(component).tuple
        (top, right, bottom, left) = self.padding(component)

        return Dimension(width + left + right, height + top + bottom)
Ejemplo n.º 2
0
 def test_bounds_size(self):
     self.assertEqual(Dimension(100, 200), Bounds(10, 20, 100, 200).size)
Ejemplo n.º 3
0
 def _reduce_size(self, s1: Dimension, s2: Dimension) -> Dimension:
     return Dimension(max(s1.width, s2.width), s1.height + s2.height)
Ejemplo n.º 4
0
        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])
Ejemplo n.º 5
0
 def _to_size(self, value: float) -> Dimension:
     return Dimension(0, value)
Ejemplo n.º 6
0
class Component(Drawable, StyleResolver, MouseEventHandler, EventDispatcher,
                ContextAware, ReactiveObject):
    visible: RP[bool] = rv.new_property()

    parent: RP[Maybe[Container]] = rv.from_value(Nothing)

    offset: RV[Point] = parent.as_view().map(lambda _, parent: parent.map(
        lambda p: rx.combine_latest(p.observe("offset"), p.observe("location"))
        .pipe(ops.map(lambda v: v[0] + v[1]))).or_else_call(lambda: rx.of(
            Point(0, 0)))).pipe(lambda _: (ops.exclusive(), ))

    _minimum_size: RP[Dimension] = rv.from_value(Dimension(0, 0))

    _preferred_size: RP[Dimension] = rv.from_value(Dimension(0, 0))

    minimum_size_override: RP[Maybe[Dimension]] = rv.from_value(Nothing)

    minimum_size: RV[Dimension] = rv.combine_latest(
        _minimum_size,
        minimum_size_override)(ops.pipe(ops.map(lambda v: v[1].value_or(v[0])),
                                        ops.distinct_until_changed()))

    preferred_size_override: RP[Maybe[Dimension]] = rv.from_value(
        Nothing).pipe(lambda o:
                      (ops.combine_latest(o.observe("minimum_size")),
                       ops.map(lambda t: t[0].map(lambda v: t[1].copy(
                           width=max(v.width, t[1].width),
                           height=max(v.height, t[1].height)))),
                       ops.distinct_until_changed()))

    preferred_size: RV[Dimension] = rv.combine_latest(
        _preferred_size, preferred_size_override, minimum_size)(ops.pipe(
            ops.map(lambda v: (v[1].value_or(v[0]), v[2])),
            ops.map(lambda v: v[0].copy(width=max(v[0].width, v[1].width),
                                        height=max(v[0].height, v[1].height))),
            ops.distinct_until_changed()))

    bounds: RP[Bounds] = Bounded.bounds.pipe(lambda o: (
        ops.combine_latest(o.observe("minimum_size")),
        ops.map(lambda v: v[0].copy(width=max(v[0].width, v[1].width),
                                    height=max(v[0].height, v[1].height))),
        ops.start_with(o.preferred_size)))

    def __init__(self, context: Context, visible: bool = True) -> None:
        if context is None:
            raise ValueError("Argument 'context' is required.")

        # noinspection PyTypeChecker
        self.visible = visible

        self._context = context
        self._valid = False
        self._ui = self.create_ui()

        assert self._ui is not None

        super().__init__()

        self.validate()

        self.ui \
            .on_invalidate(self) \
            .pipe(ops.take_until(self.on_dispose)) \
            .subscribe(lambda _: self.invalidate(), on_error=self.error_handler)

    @property
    def context(self) -> Context:
        return self._context

    @property
    def ui(self) -> ComponentUI:
        return self._ui

    @property
    def look_and_feel(self) -> LookAndFeel:
        return self.context.look_and_feel

    def create_ui(self) -> ComponentUI:
        return self.context.look_and_feel.create_ui(self)

    def show(self) -> None:
        # noinspection PyTypeChecker
        self.visible = True

    def hide(self) -> None:
        # noinspection PyTypeChecker
        self.visible = False

    @property
    def valid(self) -> bool:
        return self._valid

    # noinspection PyTypeChecker
    def validate(self, force: bool = False) -> None:
        if self.visible and (not self.valid or force):
            self._minimum_size = self.ui.minimum_size(self)
            self._preferred_size = self.ui.preferred_size(self)

            self._valid = True

            self.parent.map(lambda p: p.request_layout())

    def invalidate(self) -> None:
        self._valid = False

        self.parent.map(lambda p: p.invalidate())

    def draw(self, g: Graphics) -> None:
        if self.visible:
            g.save()

            (dx,
             dy) = self.parent.map(lambda p: p.location).value_or(Point(0, 0))
            (cx, cy, cw, ch) = self.ui.clip_bounds(self).tuple

            g.translate(dx, dy)
            g.rectangle(cx, cy, cw, ch)

            g.clip()

            try:
                self.draw_component(g)
            except BaseException as e:
                self.error_handler(e)

            g.restore()

    def draw_component(self, g: Graphics) -> None:
        self.ui.draw(g, self)

    def position_of(self, event: PositionalEvent) -> Point:
        if event is None:
            raise ValueError("Argument 'event' is required.")

        return event.position - self.offset

    @property
    def inputs(self) -> Mapping[str, Input]:
        return self.context.inputs

    @property
    def parent_dispatcher(self) -> Maybe[EventDispatcher]:
        # noinspection PyTypeChecker
        return self.parent

    def __repr__(self) -> Any:
        return str({"id": id(self), "type": type(self).__name__})
Ejemplo n.º 7
0
 def minimum_size(self, component: T) -> Dimension:
     return Dimension(0, 0)
Ejemplo n.º 8
0
 def merge(s1: Dimension, s2: Dimension):
     return Dimension(max(s1.width, s2.width),
                      max(s1.height, s2.height))
Ejemplo n.º 9
0
    def preferred_size(self, component: Canvas) -> Dimension:
        (width, height) = component.image.map(lambda i: i.size.tuple).value_or(
            (0, 0))
        (top, right, bottom, left) = self.padding(component).tuple

        return Dimension(width + left + right, height + top + bottom)