Repository: Textualize/textual
File: src/textual/view.py (Python)

Code before:
        except errors.MissingWidget as error:
            raise KeyError(str(error))

    @property
    def is_visual(self) -> bool:
        return False

    @property
    def is_root_view(self) -> bool:
        return bool(self._parent and self.parent is self.app)

    def is_mounted(self, widget: Widget) -> bool:
        return self.app.is_mounted(widget)

    def render(self) -> RenderableType:
        return self._layout

    def get_offset(self, widget: Widget) -> Offset:
        return self._layout.get_offset(widget)

    def get_arrangement(self, size: Size, scroll: Offset) -> Iterable[WidgetPlacement]:
        cached_size, cached_scroll, arrangement = self._cached_arrangement
        if cached_size == size and cached_scroll == scroll:
            return arrangement

        arrangement = []
        placements = self._layout.arrange(self, size, scroll)
        for placement in placements:
            region, widget, order = placement
            styles = widget.styles
            if styles.has_margin:
                margin = styles.margin
                arrangement.append(
                    WidgetPlacement(
                        region=region.shrink(margin),
                        widget=widget,
                        order=order,
                    )
                )
            else:
                arrangement.append(placement)

        self._cached_arrangement = (size, scroll, arrangement)
        return arrangement

    async def handle_update(self, message: messages.Update) -> None:
        if self.is_root_view:
            message.stop()
            widget = message.widget
            assert isinstance(widget, Widget)


Review comment (refactor):
I'm thinking this might simplify if we move some of the funcationality to the `WidgetPlacement` object itself.

Let's say we implement a `apply_margin` method on `WidgetPlacement` that returned either a new object or self, dependant on if there is margin or not.

Then this code could be:

```python
placements = [
    placement.apply_margin()
    for placement in self._layout.arrange(self, size, scroll)
]
```

I wouldn't be surprised if we grow other operations on the WidgetPlacement objects, and this may keep it simple.

What do you think?

Code after:
        return self.app.is_mounted(widget)

    def render(self) -> RenderableType:
        return self.layout

    def get_offset(self, widget: Widget) -> Offset:
        return self.layout.get_offset(widget)

    def get_arrangement(self, size: Size, scroll: Offset) -> Iterable[WidgetPlacement]:
        cached_size, cached_scroll, arrangement = self._cached_arrangement
        if cached_size == size and cached_scroll == scroll:
            return arrangement

        placements = [
            placement.apply_margin()
            for placement in self._layout.arrange(self, size, scroll)
        ]

        self._cached_arrangement = (size, scroll, placements)
        return placements

    async def handle_update(self, message: messages.Update) -> None:
        if self.is_root_view:
            message.stop()
            widget = message.widget
            assert isinstance(widget, Widget)

            display_update = self.layout.update_widget(self.console, widget)
            if display_update is not None:
                self.app.display(display_update)

    async def handle_layout(self, message: messages.Layout) -> None:
        await self.refresh_layout()
        if self.is_root_view:
            message.stop()
            self.app.refresh()

    def mount(self, *anon_widgets: Widget, **widgets: Widget) -> None:
        self.app.register(self, *anon_widgets, **widgets)
        self.refresh()

    async def refresh_layout(self) -> None:
        self._cached_arrangement = (Size(), Offset(), [])
        try:
            await self.layout.mount_all(self)
            if not self.is_root_view:
                await self.app.view.refresh_layout()
                return

            if not self.size:
                return