Skip to content

Commit 32b916f

Browse files
authored
fix for zero width scrollbars (#3887)
* fix for zero width scrollbars * changelog * snapshot fix * simplify * change wait * early out, test fix * no early out * increase wait * auto pilot * increase wait * skip windoze * disable test * restore test * flush events * another flush * maybe fix race condition * uderline fix * simplify * remove flush events
1 parent 1ce6473 commit 32b916f

File tree

6 files changed

+207
-9
lines changed

6 files changed

+207
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Fixed
1111

1212
- Disabled radio buttons could be selected with the keyboard https://github.com/Textualize/textual/issues/3839
13+
- Fixed zero width scrollbars causing content to disappear https://github.com/Textualize/textual/issues/3886
1314

1415
### Changed
1516

src/textual/widget.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,8 +1392,7 @@ def scrollbars_enabled(self) -> tuple[bool, bool]:
13921392
if not self.is_scrollable:
13931393
return False, False
13941394

1395-
enabled = self.show_vertical_scrollbar, self.show_horizontal_scrollbar
1396-
return enabled
1395+
return (self.show_vertical_scrollbar, self.show_horizontal_scrollbar)
13971396

13981397
@property
13991398
def scrollbars_space(self) -> tuple[int, int]:
@@ -2756,6 +2755,13 @@ def _get_scrollable_region(self, region: Region) -> Region:
27562755
scrollbar_size_horizontal = styles.scrollbar_size_horizontal
27572756
scrollbar_size_vertical = styles.scrollbar_size_vertical
27582757

2758+
show_vertical_scrollbar: bool = (
2759+
show_vertical_scrollbar and scrollbar_size_vertical
2760+
)
2761+
show_horizontal_scrollbar: bool = (
2762+
show_horizontal_scrollbar and scrollbar_size_horizontal
2763+
)
2764+
27592765
if styles.scrollbar_gutter == "stable":
27602766
# Let's _always_ reserve some space, whether the scrollbar is actually displayed or not:
27612767
show_vertical_scrollbar = True
@@ -2786,6 +2792,13 @@ def _arrange_scrollbars(self, region: Region) -> Iterable[tuple[Widget, Region]]
27862792
scrollbar_size_horizontal = self.scrollbar_size_horizontal
27872793
scrollbar_size_vertical = self.scrollbar_size_vertical
27882794

2795+
show_vertical_scrollbar: bool = (
2796+
show_vertical_scrollbar and scrollbar_size_vertical
2797+
)
2798+
show_horizontal_scrollbar: bool = (
2799+
show_horizontal_scrollbar and scrollbar_size_horizontal
2800+
)
2801+
27892802
if show_horizontal_scrollbar and show_vertical_scrollbar:
27902803
(
27912804
window_region,

src/textual/widgets/_tabs.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,17 @@ def _highlight_active(self, animate: bool = True) -> None:
593593

594594
def animate_underline() -> None:
595595
"""Animate the underline."""
596-
underline.animate("highlight_start", start, duration=0.3)
597-
underline.animate("highlight_end", end, duration=0.3)
596+
try:
597+
active_tab = self.query_one(f"#tabs-list > Tab.-active")
598+
except NoMatches:
599+
pass
600+
else:
601+
tab_region = active_tab.virtual_region.shrink(
602+
active_tab.styles.gutter
603+
)
604+
start, end = tab_region.column_span
605+
underline.animate("highlight_start", start, duration=0.3)
606+
underline.animate("highlight_end", end, duration=0.3)
598607

599608
self.set_timer(0.02, lambda: self.call_after_refresh(animate_underline))
600609
else:

tests/snapshot_tests/__snapshots__/test_snapshots.ambr

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38206,3 +38206,158 @@
3820638206

3820738207
'''
3820838208
# ---
38209+
# name: test_zero_scrollbar_size
38210+
'''
38211+
<svg class="rich-terminal" viewBox="0 0 994 635.5999999999999" xmlns="http://www.w3.org/2000/svg">
38212+
<!-- Generated with Rich https://www.textualize.io -->
38213+
<style>
38214+
38215+
@font-face {
38216+
font-family: "Fira Code";
38217+
src: local("FiraCode-Regular"),
38218+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
38219+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
38220+
font-style: normal;
38221+
font-weight: 400;
38222+
}
38223+
@font-face {
38224+
font-family: "Fira Code";
38225+
src: local("FiraCode-Bold"),
38226+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
38227+
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
38228+
font-style: bold;
38229+
font-weight: 700;
38230+
}
38231+
38232+
.terminal-2277926227-matrix {
38233+
font-family: Fira Code, monospace;
38234+
font-size: 20px;
38235+
line-height: 24.4px;
38236+
font-variant-east-asian: full-width;
38237+
}
38238+
38239+
.terminal-2277926227-title {
38240+
font-size: 18px;
38241+
font-weight: bold;
38242+
font-family: arial;
38243+
}
38244+
38245+
.terminal-2277926227-r1 { fill: #e1e1e1 }
38246+
.terminal-2277926227-r2 { fill: #c5c8c6 }
38247+
</style>
38248+
38249+
<defs>
38250+
<clipPath id="terminal-2277926227-clip-terminal">
38251+
<rect x="0" y="0" width="975.0" height="584.5999999999999" />
38252+
</clipPath>
38253+
<clipPath id="terminal-2277926227-line-0">
38254+
<rect x="0" y="1.5" width="976" height="24.65"/>
38255+
</clipPath>
38256+
<clipPath id="terminal-2277926227-line-1">
38257+
<rect x="0" y="25.9" width="976" height="24.65"/>
38258+
</clipPath>
38259+
<clipPath id="terminal-2277926227-line-2">
38260+
<rect x="0" y="50.3" width="976" height="24.65"/>
38261+
</clipPath>
38262+
<clipPath id="terminal-2277926227-line-3">
38263+
<rect x="0" y="74.7" width="976" height="24.65"/>
38264+
</clipPath>
38265+
<clipPath id="terminal-2277926227-line-4">
38266+
<rect x="0" y="99.1" width="976" height="24.65"/>
38267+
</clipPath>
38268+
<clipPath id="terminal-2277926227-line-5">
38269+
<rect x="0" y="123.5" width="976" height="24.65"/>
38270+
</clipPath>
38271+
<clipPath id="terminal-2277926227-line-6">
38272+
<rect x="0" y="147.9" width="976" height="24.65"/>
38273+
</clipPath>
38274+
<clipPath id="terminal-2277926227-line-7">
38275+
<rect x="0" y="172.3" width="976" height="24.65"/>
38276+
</clipPath>
38277+
<clipPath id="terminal-2277926227-line-8">
38278+
<rect x="0" y="196.7" width="976" height="24.65"/>
38279+
</clipPath>
38280+
<clipPath id="terminal-2277926227-line-9">
38281+
<rect x="0" y="221.1" width="976" height="24.65"/>
38282+
</clipPath>
38283+
<clipPath id="terminal-2277926227-line-10">
38284+
<rect x="0" y="245.5" width="976" height="24.65"/>
38285+
</clipPath>
38286+
<clipPath id="terminal-2277926227-line-11">
38287+
<rect x="0" y="269.9" width="976" height="24.65"/>
38288+
</clipPath>
38289+
<clipPath id="terminal-2277926227-line-12">
38290+
<rect x="0" y="294.3" width="976" height="24.65"/>
38291+
</clipPath>
38292+
<clipPath id="terminal-2277926227-line-13">
38293+
<rect x="0" y="318.7" width="976" height="24.65"/>
38294+
</clipPath>
38295+
<clipPath id="terminal-2277926227-line-14">
38296+
<rect x="0" y="343.1" width="976" height="24.65"/>
38297+
</clipPath>
38298+
<clipPath id="terminal-2277926227-line-15">
38299+
<rect x="0" y="367.5" width="976" height="24.65"/>
38300+
</clipPath>
38301+
<clipPath id="terminal-2277926227-line-16">
38302+
<rect x="0" y="391.9" width="976" height="24.65"/>
38303+
</clipPath>
38304+
<clipPath id="terminal-2277926227-line-17">
38305+
<rect x="0" y="416.3" width="976" height="24.65"/>
38306+
</clipPath>
38307+
<clipPath id="terminal-2277926227-line-18">
38308+
<rect x="0" y="440.7" width="976" height="24.65"/>
38309+
</clipPath>
38310+
<clipPath id="terminal-2277926227-line-19">
38311+
<rect x="0" y="465.1" width="976" height="24.65"/>
38312+
</clipPath>
38313+
<clipPath id="terminal-2277926227-line-20">
38314+
<rect x="0" y="489.5" width="976" height="24.65"/>
38315+
</clipPath>
38316+
<clipPath id="terminal-2277926227-line-21">
38317+
<rect x="0" y="513.9" width="976" height="24.65"/>
38318+
</clipPath>
38319+
<clipPath id="terminal-2277926227-line-22">
38320+
<rect x="0" y="538.3" width="976" height="24.65"/>
38321+
</clipPath>
38322+
</defs>
38323+
38324+
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-2277926227-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TestApp</text>
38325+
<g transform="translate(26,22)">
38326+
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
38327+
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
38328+
<circle cx="44" cy="0" r="7" fill="#28c840"/>
38329+
</g>
38330+
38331+
<g transform="translate(9, 41)" clip-path="url(#terminal-2277926227-clip-terminal)">
38332+
<rect fill="#1e1e1e" x="0" y="1.5" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="1.5" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="25.9" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="50.3" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="74.7" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="99.1" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="123.5" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="147.9" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="172.3" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="196.7" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="221.1" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="245.5" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="269.9" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="294.3" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="318.7" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="343.1" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="367.5" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="391.9" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="416.3" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="440.7" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="465.1" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="489.5" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="513.9" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="538.3" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="562.7" width="158.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="158.6" y="562.7" width="817.4" height="24.65" shape-rendering="crispEdges"/>
38333+
<g class="terminal-2277926227-matrix">
38334+
<text class="terminal-2277926227-r1" x="0" y="20" textLength="158.6" clip-path="url(#terminal-2277926227-line-0)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2277926227-line-0)">
38335+
</text><text class="terminal-2277926227-r1" x="0" y="44.4" textLength="158.6" clip-path="url(#terminal-2277926227-line-1)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2277926227-line-1)">
38336+
</text><text class="terminal-2277926227-r1" x="0" y="68.8" textLength="158.6" clip-path="url(#terminal-2277926227-line-2)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2277926227-line-2)">
38337+
</text><text class="terminal-2277926227-r1" x="0" y="93.2" textLength="158.6" clip-path="url(#terminal-2277926227-line-3)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2277926227-line-3)">
38338+
</text><text class="terminal-2277926227-r1" x="0" y="117.6" textLength="158.6" clip-path="url(#terminal-2277926227-line-4)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2277926227-line-4)">
38339+
</text><text class="terminal-2277926227-r1" x="0" y="142" textLength="158.6" clip-path="url(#terminal-2277926227-line-5)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2277926227-line-5)">
38340+
</text><text class="terminal-2277926227-r1" x="0" y="166.4" textLength="158.6" clip-path="url(#terminal-2277926227-line-6)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2277926227-line-6)">
38341+
</text><text class="terminal-2277926227-r1" x="0" y="190.8" textLength="158.6" clip-path="url(#terminal-2277926227-line-7)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2277926227-line-7)">
38342+
</text><text class="terminal-2277926227-r1" x="0" y="215.2" textLength="158.6" clip-path="url(#terminal-2277926227-line-8)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2277926227-line-8)">
38343+
</text><text class="terminal-2277926227-r1" x="0" y="239.6" textLength="158.6" clip-path="url(#terminal-2277926227-line-9)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2277926227-line-9)">
38344+
</text><text class="terminal-2277926227-r1" x="0" y="264" textLength="158.6" clip-path="url(#terminal-2277926227-line-10)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2277926227-line-10)">
38345+
</text><text class="terminal-2277926227-r1" x="0" y="288.4" textLength="158.6" clip-path="url(#terminal-2277926227-line-11)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2277926227-line-11)">
38346+
</text><text class="terminal-2277926227-r1" x="0" y="312.8" textLength="158.6" clip-path="url(#terminal-2277926227-line-12)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2277926227-line-12)">
38347+
</text><text class="terminal-2277926227-r1" x="0" y="337.2" textLength="158.6" clip-path="url(#terminal-2277926227-line-13)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2277926227-line-13)">
38348+
</text><text class="terminal-2277926227-r1" x="0" y="361.6" textLength="158.6" clip-path="url(#terminal-2277926227-line-14)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2277926227-line-14)">
38349+
</text><text class="terminal-2277926227-r1" x="0" y="386" textLength="158.6" clip-path="url(#terminal-2277926227-line-15)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2277926227-line-15)">
38350+
</text><text class="terminal-2277926227-r1" x="0" y="410.4" textLength="158.6" clip-path="url(#terminal-2277926227-line-16)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2277926227-line-16)">
38351+
</text><text class="terminal-2277926227-r1" x="0" y="434.8" textLength="158.6" clip-path="url(#terminal-2277926227-line-17)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2277926227-line-17)">
38352+
</text><text class="terminal-2277926227-r1" x="0" y="459.2" textLength="158.6" clip-path="url(#terminal-2277926227-line-18)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2277926227-line-18)">
38353+
</text><text class="terminal-2277926227-r1" x="0" y="483.6" textLength="158.6" clip-path="url(#terminal-2277926227-line-19)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2277926227-line-19)">
38354+
</text><text class="terminal-2277926227-r1" x="0" y="508" textLength="158.6" clip-path="url(#terminal-2277926227-line-20)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2277926227-line-20)">
38355+
</text><text class="terminal-2277926227-r1" x="0" y="532.4" textLength="158.6" clip-path="url(#terminal-2277926227-line-21)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2277926227-line-21)">
38356+
</text><text class="terminal-2277926227-r1" x="0" y="556.8" textLength="158.6" clip-path="url(#terminal-2277926227-line-22)">Hello,&#160;world!</text><text class="terminal-2277926227-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2277926227-line-22)">
38357+
</text><text class="terminal-2277926227-r1" x="0" y="581.2" textLength="158.6" clip-path="url(#terminal-2277926227-line-23)">Hello,&#160;world!</text>
38358+
</g>
38359+
</g>
38360+
</svg>
38361+
38362+
'''
38363+
# ---
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from textual.app import App, ComposeResult
2+
from textual.widgets import Static
3+
4+
5+
class TestApp(App):
6+
DEFAULT_CSS = """
7+
Screen {
8+
scrollbar-size: 0 0;
9+
}
10+
"""
11+
12+
def compose(self) -> ComposeResult:
13+
yield Static("Hello, world!\n" * 100)
14+
15+
16+
if __name__ == "__main__":
17+
TestApp().run()

0 commit comments

Comments
 (0)