Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/styles/scrollbar.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
57 changes: 10 additions & 47 deletions sandbox/darren/just_a_box.css
Original file line number Diff line number Diff line change
@@ -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%;
}

#right_pane {
width: 1fr;
background: #f6d55c;
}

.box:focus {
tint: cyan 40%;
}

#box1 {
background: green;
width: 140;
}

#box2 {
offset-y: 3;
background: hotpink;
}

#box3 {
background: red;
}


#box4 {
#right_pane {
background: blue;
width: 30;
}

#box5 {
background: darkviolet;
.box {
height: 12;
width: 30;
}
24 changes: 18 additions & 6 deletions sandbox/darren/just_a_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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
Expand All @@ -21,26 +22,37 @@ def render(self) -> RenderableType:


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)
Expand Down
1 change: 1 addition & 0 deletions src/textual/css/_styles_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ def process_color(self, name: str, tokens: list[Token]) -> None:
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
Expand Down
4 changes: 4 additions & 0 deletions src/textual/css/styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
14 changes: 14 additions & 0 deletions src/textual/scrollbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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
Expand Down Expand Up @@ -287,6 +288,19 @@ async def on_mouse_move(self, event: events.MouseMove) -> None:
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

Expand Down
22 changes: 20 additions & 2 deletions src/textual/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
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
Expand Down Expand Up @@ -46,6 +45,7 @@
ScrollRight,
ScrollTo,
ScrollUp,
ScrollBarCorner,
)


Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -102,6 +103,7 @@ def __init__(

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)
Expand Down Expand Up @@ -353,6 +355,19 @@ def max_scroll_y(self) -> int:
+ 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)
Expand Down Expand Up @@ -918,15 +933,18 @@ def _arrange_scrollbars(self, region: Region) -> Iterable[tuple[Widget, Region]]
_,
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:
Expand Down