mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
[css] Add tests and fix stuff for our new "scrollbar-size" CSS properties
This commit is contained in:
@@ -356,7 +356,6 @@ class Compositor:
|
||||
container_size,
|
||||
container_size,
|
||||
)
|
||||
t = 1
|
||||
|
||||
# Add the container widget, which will render a background
|
||||
map[widget] = MapGeometry(
|
||||
|
||||
@@ -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>[/]",
|
||||
examples=[
|
||||
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),
|
||||
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}[/]",
|
||||
bullets=[
|
||||
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=[
|
||||
Example(f"{property_name}: 2;"),
|
||||
],
|
||||
|
||||
@@ -8,17 +8,13 @@ from typing import cast, Iterable
|
||||
|
||||
import rich.repr
|
||||
from rich.console import RenderableType, RenderResult, Console, ConsoleOptions
|
||||
from rich.highlighter import ReprHighlighter
|
||||
from rich.markup import render
|
||||
from rich.padding import Padding
|
||||
from rich.panel import Panel
|
||||
from rich.rule import Rule
|
||||
from rich.style import Style
|
||||
from rich.syntax import Syntax
|
||||
from rich.text import Text
|
||||
|
||||
from .._loop import loop_last
|
||||
from .. import log
|
||||
from .errors import StylesheetError
|
||||
from .match import _check_selectors
|
||||
from .model import RuleSet
|
||||
|
||||
@@ -342,7 +342,10 @@ class Widget(DOMNode):
|
||||
show_horizontal = True
|
||||
elif overflow_x == "auto":
|
||||
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_vertical = self.show_vertical_scrollbar
|
||||
@@ -352,7 +355,7 @@ class Widget(DOMNode):
|
||||
show_vertical = True
|
||||
elif overflow_y == "auto":
|
||||
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
|
||||
|
||||
self.show_horizontal_scrollbar = show_horizontal
|
||||
@@ -601,7 +604,7 @@ class Widget(DOMNode):
|
||||
) = self._get_scrollbar_thicknesses()
|
||||
if show_horizontal_scrollbar and show_vertical_scrollbar:
|
||||
(region, _, _, _) = region.split(
|
||||
-horizontal_scrollbar_thickness, -vertical_scrollbar_thickness
|
||||
-vertical_scrollbar_thickness, -horizontal_scrollbar_thickness
|
||||
)
|
||||
elif show_vertical_scrollbar:
|
||||
region, _ = region.split_vertical(-vertical_scrollbar_thickness)
|
||||
@@ -635,7 +638,7 @@ class Widget(DOMNode):
|
||||
horizontal_scrollbar_region,
|
||||
_,
|
||||
) = region.split(
|
||||
-horizontal_scrollbar_thickness, -vertical_scrollbar_thickness
|
||||
-vertical_scrollbar_thickness, -horizontal_scrollbar_thickness
|
||||
)
|
||||
if vertical_scrollbar_region:
|
||||
yield self.vertical_scrollbar, vertical_scrollbar_region
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from __future__ import annotations
|
||||
from typing import cast, List
|
||||
from typing import cast, List, Sequence
|
||||
|
||||
import pytest
|
||||
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.parametrize(
|
||||
(
|
||||
"screen_size",
|
||||
"placeholders_count",
|
||||
"root_container_style",
|
||||
"placeholders_style",
|
||||
@@ -35,7 +34,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
|
||||
(
|
||||
*[
|
||||
[
|
||||
SCREEN_SIZE,
|
||||
1,
|
||||
f"border: {invisible_border_edge};", # #root has no visible border
|
||||
"", # 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")
|
||||
],
|
||||
[
|
||||
SCREEN_SIZE,
|
||||
1,
|
||||
"border: solid white;", # #root has a visible border
|
||||
"", # no specific placeholder style
|
||||
@@ -61,7 +58,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
|
||||
1,
|
||||
],
|
||||
[
|
||||
SCREEN_SIZE,
|
||||
4,
|
||||
"border: solid white;", # #root has a visible border
|
||||
"", # no specific placeholder style
|
||||
@@ -73,7 +69,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
|
||||
1,
|
||||
],
|
||||
[
|
||||
SCREEN_SIZE,
|
||||
1,
|
||||
"border: solid white;", # #root has a visible border
|
||||
"align: center top;", # placeholders are centered horizontally
|
||||
@@ -85,7 +80,6 @@ PLACEHOLDERS_DEFAULT_H = 3 # the default height for our Placeholder widgets
|
||||
1,
|
||||
],
|
||||
[
|
||||
SCREEN_SIZE,
|
||||
4,
|
||||
"border: solid white;", # #root has a visible border
|
||||
"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(
|
||||
screen_size: Size,
|
||||
placeholders_count: int,
|
||||
root_container_style: str,
|
||||
placeholders_style: str,
|
||||
@@ -136,9 +129,9 @@ async def test_composition_of_vertical_container_with_children(
|
||||
|
||||
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():
|
||||
# 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_is_a_visible_one = top_left_edge_char != " "
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user