mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Filling the gap between horizontal and vertical scrollbars (#664)
* Fill the spacing between the scrollbars * Add "scrollbar-corner-color" CSS rule and doc * Remove unused print statement * Some sandbox changes * Remove redundant words from docs * Add docstring to ScrollBarCorner class
This commit is contained in:
@@ -3,13 +3,15 @@
|
||||
There are a number of rules to set the colors used in Textual scrollbars. You won't typically need to do this, as the default themes have carefully chosen colors, but you can if you want to.
|
||||
|
||||
| Rule | Color |
|
||||
| ----------------------------- | ------------------------------------------------------- |
|
||||
|-------------------------------|---------------------------------------------------------|
|
||||
| `scrollbar-color` | Scrollbar "thumb" (movable part) |
|
||||
| `scrollbar-color-hover` | Scrollbar thumb when the mouse is hovering over it |
|
||||
| `scrollbar-color-active` | Scrollbar thumb when it is active (being dragged) |
|
||||
| `scrollbar-background` | Scrollbar background |
|
||||
| `scrollbar-background-hover` | Scrollbar background when the mouse is hovering over it |
|
||||
| `scrollbar-background-active` | Scrollbar background when the thumb is being dragged |
|
||||
| `scrollbar-corner-color` | The gap between the horizontal and vertical scrollbars |
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -1,61 +1,24 @@
|
||||
Screen {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
background: red;
|
||||
}
|
||||
|
||||
#horizontal {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.box {
|
||||
height: 5;
|
||||
width: 5;
|
||||
margin: 1 10;
|
||||
background: lightcoral;
|
||||
}
|
||||
|
||||
#left_pane {
|
||||
width: 1fr;
|
||||
background: $background;
|
||||
background: red;
|
||||
width: 20;
|
||||
overflow: scroll scroll;
|
||||
}
|
||||
|
||||
#middle_pane {
|
||||
margin-top: 4;
|
||||
width: 1fr;
|
||||
background: #173f5f;
|
||||
}
|
||||
|
||||
#middle_pane:focus {
|
||||
tint: cyan 40%;
|
||||
background: green;
|
||||
width: 140;
|
||||
}
|
||||
|
||||
#right_pane {
|
||||
width: 1fr;
|
||||
background: #f6d55c;
|
||||
}
|
||||
|
||||
.box:focus {
|
||||
tint: cyan 40%;
|
||||
}
|
||||
|
||||
#box1 {
|
||||
background: green;
|
||||
}
|
||||
|
||||
#box2 {
|
||||
offset-y: 3;
|
||||
background: hotpink;
|
||||
}
|
||||
|
||||
#box3 {
|
||||
background: red;
|
||||
}
|
||||
|
||||
|
||||
#box4 {
|
||||
background: blue;
|
||||
width: 30;
|
||||
}
|
||||
|
||||
#box5 {
|
||||
background: darkviolet;
|
||||
.box {
|
||||
height: 12;
|
||||
width: 30;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from rich.console import RenderableType
|
||||
from rich.panel import Panel
|
||||
|
||||
from textual import events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.layout import Horizontal, Vertical
|
||||
from textual.widget import Widget
|
||||
@@ -21,26 +22,37 @@ class Box(Widget, can_focus=True):
|
||||
|
||||
|
||||
class JustABox(App):
|
||||
dark = True
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Horizontal(
|
||||
Vertical(
|
||||
Box(id="box1", classes="box"),
|
||||
Box(id="box2", classes="box"),
|
||||
Box(id="box3", classes="box"),
|
||||
# Box(id="box3", classes="box"),
|
||||
# Box(id="box4", classes="box"),
|
||||
# Box(id="box5", classes="box"),
|
||||
# Box(id="box6", classes="box"),
|
||||
# Box(id="box7", classes="box"),
|
||||
# Box(id="box8", classes="box"),
|
||||
# Box(id="box9", classes="box"),
|
||||
# Box(id="box10", classes="box"),
|
||||
id="left_pane",
|
||||
),
|
||||
Box(id="middle_pane"),
|
||||
Vertical(
|
||||
Box(id="box", classes="box"),
|
||||
Box(id="box4", classes="box"),
|
||||
Box(id="box5", classes="box"),
|
||||
Box(id="boxa", classes="box"),
|
||||
Box(id="boxb", classes="box"),
|
||||
Box(id="boxc", classes="box"),
|
||||
id="right_pane",
|
||||
),
|
||||
id="horizontal",
|
||||
)
|
||||
|
||||
def key_p(self):
|
||||
print(self.query("#horizontal").first().styles.layout)
|
||||
|
||||
async def on_key(self, event: events.Key) -> None:
|
||||
await self.dispatch_key(event)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = JustABox(css_path="just_a_box.css", watch_css=True)
|
||||
|
||||
@@ -600,6 +600,7 @@ class StylesBuilder:
|
||||
process_scrollbar_color = process_color
|
||||
process_scrollbar_color_hover = process_color
|
||||
process_scrollbar_color_active = process_color
|
||||
process_scrollbar_corner_color = process_color
|
||||
process_scrollbar_background = process_color
|
||||
process_scrollbar_background_hover = process_color
|
||||
process_scrollbar_background_active = process_color
|
||||
|
||||
@@ -125,6 +125,8 @@ class RulesMap(TypedDict, total=False):
|
||||
scrollbar_color_hover: Color
|
||||
scrollbar_color_active: Color
|
||||
|
||||
scrollbar_corner_color: Color
|
||||
|
||||
scrollbar_background: Color
|
||||
scrollbar_background_hover: Color
|
||||
scrollbar_background_active: Color
|
||||
@@ -228,6 +230,8 @@ class StylesBase(ABC):
|
||||
scrollbar_color_hover = ColorProperty("ansi_yellow")
|
||||
scrollbar_color_active = ColorProperty("ansi_bright_yellow")
|
||||
|
||||
scrollbar_corner_color = ColorProperty("#666666")
|
||||
|
||||
scrollbar_background = ColorProperty("#555555")
|
||||
scrollbar_background_hover = ColorProperty("#444444")
|
||||
scrollbar_background_active = ColorProperty("black")
|
||||
|
||||
@@ -9,6 +9,7 @@ from rich.segment import Segment, Segments
|
||||
from rich.style import Style, StyleType
|
||||
|
||||
from textual.reactive import Reactive
|
||||
from textual.renderables.blank import Blank
|
||||
from . import events
|
||||
from ._types import MessageTarget
|
||||
from .geometry import Offset
|
||||
@@ -287,6 +288,19 @@ class ScrollBar(Widget):
|
||||
await self.emit(ScrollTo(self, x=x, y=y))
|
||||
|
||||
|
||||
class ScrollBarCorner(Widget):
|
||||
"""Widget which fills the gap between horizontal and vertical scrollbars,
|
||||
should they both be present."""
|
||||
|
||||
def __init__(self, name: str | None = None):
|
||||
super().__init__(name=name)
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
styles = self.parent.styles
|
||||
color = styles.scrollbar_corner_color
|
||||
return Blank(color)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import rich.repr
|
||||
from rich.align import Align
|
||||
from rich.console import Console, RenderableType
|
||||
from rich.measure import Measurement
|
||||
|
||||
from rich.segment import Segment
|
||||
from rich.style import Style
|
||||
from rich.styled import Styled
|
||||
@@ -46,6 +45,7 @@ if TYPE_CHECKING:
|
||||
ScrollRight,
|
||||
ScrollTo,
|
||||
ScrollUp,
|
||||
ScrollBarCorner,
|
||||
)
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ class Widget(DOMNode):
|
||||
scrollbar-background-hover: $panel-darken-2;
|
||||
scrollbar-color: $primary-lighten-1;
|
||||
scrollbar-color-active: $warning-darken-1;
|
||||
scrollbar-corner-color: $panel-darken-3;
|
||||
scrollbar-size-vertical: 2;
|
||||
scrollbar-size-horizontal: 1;
|
||||
}
|
||||
@@ -102,6 +103,7 @@ class Widget(DOMNode):
|
||||
|
||||
self._vertical_scrollbar: ScrollBar | None = None
|
||||
self._horizontal_scrollbar: ScrollBar | None = None
|
||||
self._scrollbar_corner: ScrollBarCorner | None = None
|
||||
|
||||
self._render_cache = RenderCache(Size(0, 0), [])
|
||||
# Regions which need to be updated (in Widget)
|
||||
@@ -353,6 +355,19 @@ class Widget(DOMNode):
|
||||
+ self.scrollbar_size_horizontal,
|
||||
)
|
||||
|
||||
@property
|
||||
def scrollbar_corner(self) -> ScrollBarCorner:
|
||||
"""Return the ScrollBarCorner - the cells that appear between the
|
||||
horizontal and vertical scrollbars (only when both are visible).
|
||||
"""
|
||||
from .scrollbar import ScrollBarCorner
|
||||
|
||||
if self._scrollbar_corner is not None:
|
||||
return self._scrollbar_corner
|
||||
self._scrollbar_corner = ScrollBarCorner()
|
||||
self.app.start_widget(self, self._scrollbar_corner)
|
||||
return self._scrollbar_corner
|
||||
|
||||
@property
|
||||
def vertical_scrollbar(self) -> ScrollBar:
|
||||
"""Get a vertical scrollbar (create if necessary)
|
||||
@@ -918,15 +933,18 @@ class Widget(DOMNode):
|
||||
_,
|
||||
vertical_scrollbar_region,
|
||||
horizontal_scrollbar_region,
|
||||
_,
|
||||
scrollbar_corner_gap,
|
||||
) = region.split(
|
||||
-scrollbar_size_vertical,
|
||||
-scrollbar_size_horizontal,
|
||||
)
|
||||
if scrollbar_corner_gap:
|
||||
yield self.scrollbar_corner, scrollbar_corner_gap
|
||||
if vertical_scrollbar_region:
|
||||
yield self.vertical_scrollbar, vertical_scrollbar_region
|
||||
if horizontal_scrollbar_region:
|
||||
yield self.horizontal_scrollbar, horizontal_scrollbar_region
|
||||
|
||||
elif show_vertical_scrollbar:
|
||||
_, scrollbar_region = region.split_vertical(-scrollbar_size_vertical)
|
||||
if scrollbar_region:
|
||||
|
||||
Reference in New Issue
Block a user