[css] Add tests and fix stuff for our new "scrollbar-size" CSS properties

This commit is contained in:
Olivier Philippon
2022-05-19 12:16:10 +01:00
parent 1c76aa7be9
commit 9e6140af7c
5 changed files with 97 additions and 22 deletions

View File

@@ -356,7 +356,6 @@ class Compositor:
container_size, container_size,
container_size, container_size,
) )
t = 1
# Add the container widget, which will render a background # Add the container widget, which will render a background
map[widget] = MapGeometry( map[widget] = MapGeometry(

View File

@@ -640,13 +640,13 @@ def scrollbar_size_property_help_text(context: StylingContext) -> HelpText:
markup="The [i]scrollbar-size[/] property expects a value of the form [i]<horizontal> <vertical>[/]", markup="The [i]scrollbar-size[/] property expects a value of the form [i]<horizontal> <vertical>[/]",
examples=[ examples=[
Example( Example(
"scrollbar-size: 2 3; [dim]# Horizontal offset of 2, vertical offset of 3" "scrollbar-size: 2 3; [dim]# Horizontal size of 2, vertical size of 3"
), ),
], ],
), ),
], ],
).get_by_context(context), ).get_by_context(context),
Bullet("<horizontal> and <vertical> must be integers"), Bullet("<horizontal> and <vertical> must be positive integers"),
], ],
) )
@@ -664,7 +664,7 @@ def scrollbar_size_single_axis_help_text(property_name: str) -> HelpText:
summary=f"Invalid value for [i]{property_name}[/]", summary=f"Invalid value for [i]{property_name}[/]",
bullets=[ bullets=[
Bullet( Bullet(
markup=f"The [i]{property_name}[/] property can only be set to a integer", markup=f"The [i]{property_name}[/] property can only be set to a positive integer",
examples=[ examples=[
Example(f"{property_name}: 2;"), Example(f"{property_name}: 2;"),
], ],

View File

@@ -8,17 +8,13 @@ from typing import cast, Iterable
import rich.repr import rich.repr
from rich.console import RenderableType, RenderResult, Console, ConsoleOptions from rich.console import RenderableType, RenderResult, Console, ConsoleOptions
from rich.highlighter import ReprHighlighter
from rich.markup import render from rich.markup import render
from rich.padding import Padding from rich.padding import Padding
from rich.panel import Panel from rich.panel import Panel
from rich.rule import Rule
from rich.style import Style from rich.style import Style
from rich.syntax import Syntax from rich.syntax import Syntax
from rich.text import Text from rich.text import Text
from .._loop import loop_last
from .. import log
from .errors import StylesheetError from .errors import StylesheetError
from .match import _check_selectors from .match import _check_selectors
from .model import RuleSet from .model import RuleSet

View File

@@ -342,7 +342,10 @@ class Widget(DOMNode):
show_horizontal = True show_horizontal = True
elif overflow_x == "auto": elif overflow_x == "auto":
show_horizontal = self.virtual_size.width > width show_horizontal = self.virtual_size.width > width
if scrollbar_size_horizontal == 0: if (
scrollbar_size_horizontal is not None
and scrollbar_size_horizontal.value == 0
):
show_horizontal = False show_horizontal = False
show_vertical = self.show_vertical_scrollbar show_vertical = self.show_vertical_scrollbar
@@ -352,7 +355,7 @@ class Widget(DOMNode):
show_vertical = True show_vertical = True
elif overflow_y == "auto": elif overflow_y == "auto":
show_vertical = self.virtual_size.height > height show_vertical = self.virtual_size.height > height
if scrollbar_size_vertical == 0: if scrollbar_size_vertical is not None and scrollbar_size_vertical.value == 0:
show_vertical = False show_vertical = False
self.show_horizontal_scrollbar = show_horizontal self.show_horizontal_scrollbar = show_horizontal
@@ -601,7 +604,7 @@ class Widget(DOMNode):
) = self._get_scrollbar_thicknesses() ) = self._get_scrollbar_thicknesses()
if show_horizontal_scrollbar and show_vertical_scrollbar: if show_horizontal_scrollbar and show_vertical_scrollbar:
(region, _, _, _) = region.split( (region, _, _, _) = region.split(
-horizontal_scrollbar_thickness, -vertical_scrollbar_thickness -vertical_scrollbar_thickness, -horizontal_scrollbar_thickness
) )
elif show_vertical_scrollbar: elif show_vertical_scrollbar:
region, _ = region.split_vertical(-vertical_scrollbar_thickness) region, _ = region.split_vertical(-vertical_scrollbar_thickness)
@@ -635,7 +638,7 @@ class Widget(DOMNode):
horizontal_scrollbar_region, horizontal_scrollbar_region,
_, _,
) = region.split( ) = region.split(
-horizontal_scrollbar_thickness, -vertical_scrollbar_thickness -vertical_scrollbar_thickness, -horizontal_scrollbar_thickness
) )
if vertical_scrollbar_region: if vertical_scrollbar_region:
yield self.vertical_scrollbar, vertical_scrollbar_region yield self.vertical_scrollbar, vertical_scrollbar_region

View File

@@ -1,5 +1,5 @@
from __future__ import annotations from __future__ import annotations
from typing import cast, List from typing import cast, List, Sequence
import pytest import pytest
from rich.console import RenderableType from rich.console import RenderableType
@@ -24,7 +24,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
@pytest.mark.integration_test # this is a slow test, we may want to skip them in some contexts @pytest.mark.integration_test # this is a slow test, we may want to skip them in some contexts
@pytest.mark.parametrize( @pytest.mark.parametrize(
( (
"screen_size",
"placeholders_count", "placeholders_count",
"root_container_style", "root_container_style",
"placeholders_style", "placeholders_style",
@@ -35,7 +34,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
( (
*[ *[
[ [
SCREEN_SIZE,
1, 1,
f"border: {invisible_border_edge};", # #root has no visible border f"border: {invisible_border_edge};", # #root has no visible border
"", # no specific placeholder style "", # no specific placeholder style
@@ -49,7 +47,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
for invisible_border_edge in ("", "none", "hidden") for invisible_border_edge in ("", "none", "hidden")
], ],
[ [
SCREEN_SIZE,
1, 1,
"border: solid white;", # #root has a visible border "border: solid white;", # #root has a visible border
"", # no specific placeholder style "", # no specific placeholder style
@@ -61,7 +58,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
1, 1,
], ],
[ [
SCREEN_SIZE,
4, 4,
"border: solid white;", # #root has a visible border "border: solid white;", # #root has a visible border
"", # no specific placeholder style "", # no specific placeholder style
@@ -73,7 +69,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
1, 1,
], ],
[ [
SCREEN_SIZE,
1, 1,
"border: solid white;", # #root has a visible border "border: solid white;", # #root has a visible border
"align: center top;", # placeholders are centered horizontally "align: center top;", # placeholders are centered horizontally
@@ -85,7 +80,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
1, 1,
], ],
[ [
SCREEN_SIZE,
4, 4,
"border: solid white;", # #root has a visible border "border: solid white;", # #root has a visible border
"align: center top;", # placeholders are centered horizontally "align: center top;", # placeholders are centered horizontally
@@ -99,7 +93,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
), ),
) )
async def test_composition_of_vertical_container_with_children( async def test_composition_of_vertical_container_with_children(
screen_size: Size,
placeholders_count: int, placeholders_count: int,
root_container_style: str, root_container_style: str,
placeholders_style: str, placeholders_style: str,
@@ -136,9 +129,9 @@ async def test_composition_of_vertical_container_with_children(
yield VerticalContainer(*placeholders, id="root") yield VerticalContainer(*placeholders, id="root")
app = MyTestApp(size=screen_size, test_name="compositor") app = MyTestApp(size=SCREEN_SIZE, test_name="compositor")
expected_screen_size = Size(*screen_size) expected_screen_size = SCREEN_SIZE
async with app.in_running_state(): async with app.in_running_state():
# root widget checks: # root widget checks:
@@ -232,3 +225,87 @@ async def test_border_edge_types_impact_on_widget_size(
top_left_edge_char = app.get_char_at(0, 0) top_left_edge_char = app.get_char_at(0, 0)
top_left_edge_char_is_a_visible_one = top_left_edge_char != " " top_left_edge_char_is_a_visible_one = top_left_edge_char != " "
assert top_left_edge_char_is_a_visible_one == expects_visible_char_at_top_left_edge assert top_left_edge_char_is_a_visible_one == expects_visible_char_at_top_left_edge
@pytest.mark.asyncio
@pytest.mark.parametrize(
"large_widget_size,container_style,expected_large_widget_visible_region_size",
(
# In these tests we're going to insert a "large widget"
# into a container with size (20,20).
# ---------------- let's start!
# no overflow/scrollbar instructions: no scrollbars
[Size(30, 30), "color: red", Size(20, 20)],
# explicit hiding of the overflow: no scrollbars either
[Size(30, 30), "overflow: hidden", Size(20, 20)],
# scrollbar for both directions
[Size(30, 30), "overflow: auto", Size(19, 19)],
# horizontal scrollbar
[Size(30, 30), "overflow-x: auto", Size(20, 19)],
# vertical scrollbar
[Size(30, 30), "overflow-y: auto", Size(19, 20)],
# scrollbar for both directions, custom scrollbar size
[Size(30, 30), ("overflow: auto", "scrollbar-size: 3 5"), Size(15, 17)],
# scrollbar for both directions, custom vertical scrollbar size
[Size(30, 30), ("overflow: auto", "scrollbar-size-vertical: 3"), Size(17, 19)],
# scrollbar for both directions, custom horizontal scrollbar size
[
Size(30, 30),
("overflow: auto", "scrollbar-size-horizontal: 3"),
Size(19, 17),
],
# scrollbar needed only vertically, custom scrollbar size
[
Size(20, 30),
("overflow: auto", "scrollbar-size: 3 3"),
Size(17, 20),
],
# scrollbar needed only horizontally, custom scrollbar size
[
Size(30, 20),
("overflow: auto", "scrollbar-size: 3 3"),
Size(20, 17),
],
# edge case: scrollbars should not be displayed at all if their size is set to 0
[Size(30, 30), ("overflow: auto", "scrollbar-size: 0 0"), Size(20, 20)],
),
)
async def test_scrollbar_size_impact_on_the_layout(
large_widget_size: Size,
container_style: str | Sequence[str],
expected_large_widget_visible_region_size: Size,
):
class LargeWidget(Widget):
def on_mount(self):
self.styles.width = large_widget_size[0]
self.styles.height = large_widget_size[1]
class LargeWidgetContainer(Widget):
CSS = """
LargeWidgetContainer {
width: 20;
height: 20;
${container_style};
}
""".replace(
"${container_style}",
container_style
if isinstance(container_style, str)
else ";".join(container_style),
)
large_widget = LargeWidget()
container = LargeWidgetContainer(large_widget)
class MyTestApp(AppTest):
def compose(self) -> ComposeResult:
yield container
app = MyTestApp(size=Size(40, 40), test_name="scrollbar_size_impact_on_the_layout")
await app.boot_and_shutdown()
compositor = app.screen._compositor
widgets_map = compositor.map
large_widget_visible_region_size = widgets_map[large_widget].visible_region.size
assert large_widget_visible_region_size == expected_large_widget_visible_region_size