diff --git a/rio/debug/dev_tools/layout_explainer.py b/rio/debug/dev_tools/layout_explainer.py index 66080c21..a5dc876d 100644 --- a/rio/debug/dev_tools/layout_explainer.py +++ b/rio/debug/dev_tools/layout_explainer.py @@ -40,7 +40,7 @@ FULL_SIZE_SINGLE_CONTAINERS: set[type[rio.Component]] = { # These components make use of the `grow_...` attributes in at least one axis. -CONTAINERS_SUPPORTING_GROW: t.Iterable[type[rio.Component]] = { +CONTAINERS_SUPPORTING_GROW: set[type[rio.Component]] = { rio.Column, rio.Grid, rio.Row, @@ -155,11 +155,11 @@ class LayoutExplainer: axis_xy = "y" target_class_name = type(self.component).__name__ + parent_class_name = type(self._parent).__name__ # Try to get a specialized explanation based on the parent - # self.component if self._parent_layout is None: - return f"The self.component was allocated a {axis_name} of {allocated_space_before_alignment:.1f} by its parent." + return f"The component was allocated a {axis_name} of {allocated_space_before_alignment:.1f} by its parent." # Prepare some commonly used values if axis_name == "width": @@ -171,8 +171,6 @@ class LayoutExplainer: parent_natural_size = self._parent_layout.natural_height is_grower = self.component.grow_y - parent_class_name = type(self._parent).__name__ - # Toplevel? if self.component is self.session._get_user_root_component(): return f"This is the app's top-level component. As such, the {target_class_name} was allocated the full {axis_name} of {allocated_space_before_alignment:.1f} available in the window." @@ -310,6 +308,7 @@ class LayoutExplainer: is_grower = self.component.grow_y target_class_name = type(self.component).__name__ + parent_class_name = type(self._parent).__name__ # Warn if the component has a `grow` attribute set, but is placed inside # of a container that doesn't support it @@ -319,7 +318,7 @@ class LayoutExplainer: and type(self._parent) not in CONTAINERS_SUPPORTING_GROW ): self.warnings.append( - f"The component has `grow_{axis_xy}=True` set, but is placed inside of a `{type(self._parent).__name__}`. {type(self._parent).__name__} components do not make use of this property, so it has no effect." + f"The component has `grow_{axis_xy}=True` set, but is placed inside of a `{parent_class_name}`. {parent_class_name} components do not make use of this property, so it has no effect." ) # Warn if the component is aligned, but has no natural size @@ -372,7 +371,7 @@ class LayoutExplainer: # Warn if the specified minimum size is less than the natural one if 0 < specified_min_size < natural_size: self.warnings.append( - f"\n\nThe explicitly set minimum {axis_name} of {specified_min_size:.1f} has no effect, because it is less than the component's natural {axis_name} of {natural_size:.1f}. Components can never be smaller than their natural size." + f"The explicitly set minimum {axis_name} of {specified_min_size:.1f} has no effect, because it is less than the component's natural {axis_name} of {natural_size:.1f}. Components can never be smaller than their natural size." ) # Suggest growing the component by setting an explicit size @@ -413,5 +412,20 @@ class LayoutExplainer: f"Remove the `align_{axis_xy}` attribute from the component, so it takes up all of the available space" ) + # If the component is at its minimum size, give tips on how to reduce + # its minimum size further + if allocated_size < natural_size + 0.1: + for suggestion in self._explain_how_to_reduce_natural_size( + axis_name + ): + suggest_shrink(suggestion) + # Done! return result.getvalue() + + def _explain_how_to_reduce_natural_size( + self, axis_name: t.Literal["width", "height"] + ) -> t.Iterable[str]: + if isinstance(self.component, rio.Text): + if axis_name == "width": + yield 'Set `overflow="wrap"` or `overflow="ellipsize"` to reduce the `Text`\'s natural width' diff --git a/rio/debug/dev_tools/layout_subpage.py b/rio/debug/dev_tools/layout_subpage.py index b695f1c5..34110d56 100644 --- a/rio/debug/dev_tools/layout_subpage.py +++ b/rio/debug/dev_tools/layout_subpage.py @@ -141,7 +141,8 @@ class HelpAnchor(rio.Component): class ActionAnchor(rio.Component): icon: str - partial_name: str + action_name: str # Something like "shrink" or "reduce" or "increase" + dimension_name: t.Literal["width", "height"] actions: list[str] def build(self) -> rio.Component: @@ -154,7 +155,7 @@ class ActionAnchor(rio.Component): anchor = rio.Row( rio.Icon(self.icon, fill=color), rio.Text( - f"How to {self.partial_name}", + f"How to {self.action_name} {self.dimension_name}", style=rio.TextStyle(fill=color), ), spacing=0.3, @@ -162,12 +163,10 @@ class ActionAnchor(rio.Component): margin=0.5, ) - # If there aren't any options there is no need to go any further + # Prepare the markdown content (for the tooltip) if not self.actions: - return anchor - - # Prepare the markdown content - if len(self.actions) == 1: + markdown_source = f"The component's {self.dimension_name} is already at its minimum. It cannot be reduced further." + elif len(self.actions) == 1: markdown_source = self.actions[0] else: markdown_source = "- " + "\n- ".join(self.actions) @@ -258,12 +257,14 @@ class LayoutSubpage(rio.Component): rio.Row( ActionAnchor( "close-fullscreen", - "shrink width", + "shrink", + "width", self._layout_explainer.decrease_width, ), ActionAnchor( "open-in-full", - "grow width", + "grow", + "width", self._layout_explainer.increase_width, ), ) @@ -280,12 +281,14 @@ class LayoutSubpage(rio.Component): rio.Row( ActionAnchor( "close-fullscreen", - "shrink height", + "shrink", + "height", self._layout_explainer.decrease_height, ), ActionAnchor( "open-in-full", - "grow height", + "grow", + "height", self._layout_explainer.increase_height, ), )