Initial layout guide stuff (#748)

* Initial layout guide stuff

* More docs on layout - grid

* Continuing grid docs

* Grid gutter and spans guide

* Improvements to layout docs for horizontal, vertical, dock, and begin describing layers

* Update center layout example to reflect new yield order

* More updates to layout guide, mostly offset stuff

* More layout guide, "Putting it all together"

* Updates to layout guide page

* Small rewording of dock layout in guide

* Apostrophe

* Typo

* Small design tweak to combining layouts example

* Typos, tidying up

* Small reword

* Some updates to docs/guide/layout/grid

* calc fix

Co-authored-by: Will McGugan <willmcgugan@gmail.com>
This commit is contained in:
darrenburns
2022-09-15 15:28:23 +01:00
committed by GitHub
parent e2ff19e1f3
commit 5ef98aac90
42 changed files with 1223 additions and 29 deletions

View File

@@ -0,0 +1,21 @@
Screen {
layout: center;
}
#bottom {
width: 20;
height: 20;
background: red;
}
#middle {
width: 26;
height: 12;
background: green;
}
#top {
width: 32;
height: 6;
background: blue;
}

View File

@@ -0,0 +1,14 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class CenterLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", id="bottom")
yield Static("Two", id="middle")
yield Static("Three", id="top")
app = CenterLayoutExample(css_path="center_layout.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,50 @@
#app-grid {
layout: grid;
grid-size: 2; /* two columns */
grid-columns: 1fr;
grid-rows: 1fr;
}
#left-pane > Static {
background: $boost;
color: auto;
margin-bottom: 1;
padding: 1;
}
#left-pane {
row-span: 2;
background: $panel;
border: dodgerblue;
}
#top-right {
background: $panel;
border: mediumvioletred;
}
#top-right > Static {
width: auto;
height: 100%;
margin-right: 1;
background: $boost;
}
#bottom-right {
layout: grid;
grid-size: 3;
grid-columns: 1fr;
grid-rows: 1fr;
grid-gutter: 1;
background: $panel;
border: greenyellow;
}
#bottom-right-final {
column-span: 2;
}
#bottom-right > Static {
height: 100%;
background: $boost;
}

View File

@@ -0,0 +1,43 @@
from textual import layout
from textual.app import ComposeResult, App
from textual.widgets import Static, Header
class CombiningLayoutsExample(App):
def compose(self) -> ComposeResult:
yield Header()
yield layout.Container(
layout.Vertical(
*[Static(f"Vertical layout, child {number}") for number in range(15)],
id="left-pane",
),
layout.Horizontal(
Static("Horizontally"),
Static("Positioned"),
Static("Children"),
Static("Here"),
id="top-right",
),
layout.Container(
Static("This"),
Static("panel"),
Static("is"),
Static("using"),
Static("grid layout!", id="bottom-right-final"),
id="bottom-right",
),
id="app-grid",
)
async def on_key(self, event) -> None:
await self.dispatch_key(event)
def key_a(self):
print(self.stylesheet.variables["boost"])
print(self.stylesheet.variables["boost-lighten-1"])
print(self.stylesheet.variables["boost-lighten-2"])
app = CombiningLayoutsExample(css_path="combining_layouts.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,7 @@
#sidebar {
dock: left;
width: 15;
height: 100%;
color: #0f2b41;
background: dodgerblue;
}

View File

@@ -0,0 +1,20 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
TEXT = """\
Docking a widget removes it from the layout and fixes its position, aligned to either the top, right, bottom, or left edges of a container.
Docked widgets will not scroll out of view, making them ideal for sticky headers, footers, and sidebars.
"""
class DockLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("Sidebar", id="sidebar")
yield Static(TEXT * 10, id="body")
app = DockLayoutExample(css_path="dock_layout1_sidebar.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,14 @@
#another-sidebar {
dock: left;
width: 30;
height: 100%;
background: deeppink;
}
#sidebar {
dock: left;
width: 15;
height: 100%;
color: #0f2b41;
background: dodgerblue;
}

View File

@@ -0,0 +1,21 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
TEXT = """\
Docking a widget removes it from the layout and fixes its position, aligned to either the top, right, bottom, or left edges of a container.
Docked widgets will not scroll out of view, making them ideal for sticky headers, footers, and sidebars.
"""
class DockLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("Sidebar2", id="another-sidebar")
yield Static("Sidebar1", id="sidebar")
yield Static(TEXT * 10, id="body")
app = DockLayoutExample(css_path="dock_layout2_sidebar.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,7 @@
#sidebar {
dock: left;
width: 15;
height: 100%;
color: #0f2b41;
background: dodgerblue;
}

View File

@@ -0,0 +1,21 @@
from textual.app import App, ComposeResult
from textual.widgets import Static, Header
TEXT = """\
Docking a widget removes it from the layout and fixes its position, aligned to either the top, right, bottom, or left edges of a container.
Docked widgets will not scroll out of view, making them ideal for sticky headers, footers, and sidebars.
"""
class DockLayoutExample(App):
def compose(self) -> ComposeResult:
yield Header(id="header")
yield Static("Sidebar1", id="sidebar")
yield Static(TEXT * 10, id="body")
app = DockLayoutExample(css_path="dock_layout3_sidebar_header.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,9 @@
Screen {
layout: grid;
grid-size: 3;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
app = GridLayoutExample(css_path="grid_layout1.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,9 @@
Screen {
layout: grid;
grid-size: 3;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,18 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
yield Static("Seven", classes="box")
app = GridLayoutExample(css_path="grid_layout1.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,10 @@
Screen {
layout: grid;
grid-size: 3;
grid-columns: 2fr 1fr 1fr;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
app = GridLayoutExample(css_path="grid_layout3_row_col_adjust.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,11 @@
Screen {
layout: grid;
grid-size: 3;
grid-columns: 2fr 1fr 1fr;
grid-rows: 25% 75%;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
app = GridLayoutExample(css_path="grid_layout4_row_col_adjust.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,14 @@
Screen {
layout: grid;
grid-size: 3;
}
#two {
column-span: 2;
tint: magenta 40%;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two [b](column-span: 2)", classes="box", id="two")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
app = GridLayoutExample(css_path="grid_layout5_col_span.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,15 @@
Screen {
layout: grid;
grid-size: 3;
}
#two {
column-span: 2;
row-span: 2;
tint: magenta 40%;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two [b](column-span: 2 and row-span: 2)", classes="box", id="two")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
app = GridLayoutExample(css_path="grid_layout6_row_span.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,11 @@
Screen {
layout: grid;
grid-size: 3;
grid-gutter: 1;
background: lightgreen;
}
.box {
background: darkmagenta;
height: 100%;
}

View File

@@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class GridLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
yield Static("Four", classes="box")
yield Static("Five", classes="box")
yield Static("Six", classes="box")
app = GridLayoutExample(css_path="grid_layout7_gutter.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,9 @@
Screen {
layout: horizontal;
}
.box {
height: 100%;
width: 1fr;
border: solid green;
}

View File

@@ -0,0 +1,14 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class HorizontalLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
app = HorizontalLayoutExample(css_path="horizontal_layout.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,9 @@
Screen {
layout: horizontal;
overflow-x: auto;
}
.box {
height: 100%;
border: solid green;
}

View File

@@ -0,0 +1,14 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class HorizontalLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
app = HorizontalLayoutExample(css_path="horizontal_layout_overflow.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,22 @@
Screen {
layout: center;
layers: below above;
}
Static {
width: 28;
height: 8;
color: auto;
content-align: center middle;
}
#box1 {
background: darkcyan;
layer: above;
}
#box2 {
layer: below;
background: orange;
offset: 12 6;
}

View File

@@ -0,0 +1,13 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class LayersExample(App):
def compose(self) -> ComposeResult:
yield Static("box1 (layer = above)", id="box1")
yield Static("box2 (layer = below)", id="box2")
app = LayersExample(css_path="layers.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,36 @@
Screen {
layout: center;
}
#parent {
layout: center;
background: #9e9e9e;
width: 60;
height: 20;
}
Box {
color: auto;
width: auto;
height: auto;
padding: 1 2;
}
#box1 {
background: $primary;
}
#box2 {
background: $secondary;
offset: 12 4;
}
#box3 {
background: lightseagreen;
offset: -12 -4;
}
#box4 {
background: darkred;
offset: -26 10;
}

View File

@@ -0,0 +1,27 @@
from rich.console import RenderableType
from textual import layout
from textual.app import App, ComposeResult
from textual.widgets import Static
class Box(Static):
def render(self) -> RenderableType:
x, y = self.styles.offset
return f"{self.id}: offset = ({x}, {y})"
class OffsetExample(App):
def compose(self) -> ComposeResult:
yield layout.Container(
Box(id="box1"),
Box(id="box2"),
Box(id="box3"),
Box(id="box4"),
id="parent",
)
app = OffsetExample(css_path="offset.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,10 @@
Static {
content-align: center middle;
background: crimson;
border: solid darkred;
height: 1fr;
}
.column {
width: 1fr;
}

View File

@@ -0,0 +1,24 @@
from textual import layout
from textual.app import App, ComposeResult
from textual.widgets import Static
class UtilityContainersExample(App):
def compose(self) -> ComposeResult:
yield layout.Horizontal(
layout.Vertical(
Static("One"),
Static("Two"),
classes="column",
),
layout.Vertical(
Static("Three"),
Static("Four"),
classes="column",
),
)
app = UtilityContainersExample(css_path="utility_containers.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,8 @@
Screen {
layout: vertical;
}
.box {
height: 1fr;
border: solid green;
}

View File

@@ -0,0 +1,14 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class VerticalLayoutExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
app = VerticalLayoutExample(css_path="vertical_layout.css")
if __name__ == "__main__":
app.run()

View File

@@ -0,0 +1,8 @@
Screen {
layout: vertical;
}
.box {
height: 14;
border: solid green;
}

View File

@@ -0,0 +1,14 @@
from textual.app import App, ComposeResult
from textual.widgets import Static
class VerticalLayoutScrolledExample(App):
def compose(self) -> ComposeResult:
yield Static("One", classes="box")
yield Static("Two", classes="box")
yield Static("Three", classes="box")
app = VerticalLayoutScrolledExample(css_path="vertical_layout_scrolled.css")
if __name__ == "__main__":
app.run()