mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
button widget
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from textual.app import App
|
||||
from textual import events
|
||||
from textual.view import View
|
||||
from textual.widgets import Placeholder
|
||||
from textual.widgets import Button, Placeholder
|
||||
from textual.layouts.grid import GridLayout
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class GridTest(App):
|
||||
await self.push_view(View(layout=layout))
|
||||
|
||||
layout.add_column("col", max_size=20, repeat=4)
|
||||
layout.add_row("numbers", max_size=10)
|
||||
layout.add_row("numbers", min_size=3, max_size=10)
|
||||
layout.add_row("row", max_size=10, repeat=4)
|
||||
|
||||
layout.add_areas(
|
||||
@@ -27,9 +27,9 @@ class GridTest(App):
|
||||
|
||||
layout.place(
|
||||
numbers=Placeholder(name="numbers"),
|
||||
zero=Placeholder(name="0"),
|
||||
dot=Placeholder(name="."),
|
||||
equals=Placeholder(name="="),
|
||||
zero=Button("0"),
|
||||
dot=Button("."),
|
||||
equals=Button("="),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -22,15 +22,19 @@ class GridTest(App):
|
||||
layout.add_row(fraction=2, name="middle")
|
||||
layout.add_row(fraction=1, name="bottom")
|
||||
|
||||
layout.add_area("area1", "left", "top")
|
||||
layout.add_area("area2", "center", "middle")
|
||||
layout.add_area("area3", ("left-start", "right-end"), "bottom")
|
||||
layout.add_area("area4", "right", ("top-start", "middle-end"))
|
||||
layout.add_areas(
|
||||
area1="left,top",
|
||||
area2="center,middle",
|
||||
area3="left-start|right-end,bottom",
|
||||
area4="right,top-start|middle-end",
|
||||
)
|
||||
|
||||
await view.mount(layout.add_widget(Placeholder(name="area1"), "area1"))
|
||||
await view.mount(layout.add_widget(Placeholder(name="area2"), "area2"))
|
||||
await view.mount(layout.add_widget(Placeholder(name="area3"), "area3"))
|
||||
await view.mount(layout.add_widget(Placeholder(name="area4"), "area4"))
|
||||
layout.place(
|
||||
area1=Placeholder(name="area1"),
|
||||
area2=Placeholder(name="area2"),
|
||||
area3=Placeholder(name="area3"),
|
||||
area4=Placeholder(name="area4"),
|
||||
)
|
||||
|
||||
|
||||
GridTest.run(title="Grid Test")
|
||||
0
examples/live.py
Normal file
0
examples/live.py
Normal file
@@ -19,7 +19,7 @@ class Edge(Protocol):
|
||||
|
||||
|
||||
def layout_resolve(total: int, edges: Sequence[Edge]) -> List[int]:
|
||||
"""Divide total space to satisfy size, ratio, and minimum_size, constraints.
|
||||
"""Divide total space to satisfy size, fraction, and min_size, constraints.
|
||||
|
||||
The returned list of integers should add up to total in most cases, unless it is
|
||||
impossible to satisfy all the constraints. For instance, if there are two edges
|
||||
|
||||
@@ -122,6 +122,10 @@ class Layout(ABC):
|
||||
hidden=hidden_widgets, shown=shown_widgets, resized=resized_widgets
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def get_widgets(self) -> Iterable[Widget]:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def generate_map(
|
||||
self, width: int, height: int, offset: Point = Point(0, 0)
|
||||
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Sequence
|
||||
from typing import Iterable, TYPE_CHECKING, Sequence
|
||||
|
||||
from .._layout_resolve import layout_resolve
|
||||
from ..geometry import Region, Point
|
||||
@@ -29,8 +29,7 @@ DockEdge = Literal["top", "right", "bottom", "left"]
|
||||
class DockOptions:
|
||||
size: int | None = None
|
||||
fraction: int = 1
|
||||
minimum_size: int = 1
|
||||
maximim_size: int | None = None
|
||||
min_size: int = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -45,6 +44,10 @@ class DockLayout(Layout):
|
||||
self.docks: list[Dock] = docks or []
|
||||
super().__init__()
|
||||
|
||||
def get_widgets(self) -> Iterable[Widget]:
|
||||
for dock in self.docks:
|
||||
yield from dock.widgets
|
||||
|
||||
def generate_map(
|
||||
self, width: int, height: int, offset: Point = Point(0, 0)
|
||||
) -> dict[Widget, OrderedRegion]:
|
||||
@@ -67,9 +70,7 @@ class DockLayout(Layout):
|
||||
for index, dock in enumerate(self.docks):
|
||||
dock_options = [
|
||||
DockOptions(
|
||||
widget.layout_size,
|
||||
widget.layout_fraction,
|
||||
widget.layout_minimim_size,
|
||||
widget.layout_size, widget.layout_fraction, widget.layout_min_size
|
||||
)
|
||||
for widget in dock.widgets
|
||||
]
|
||||
|
||||
@@ -210,13 +210,15 @@ class GridLayout(Layout):
|
||||
offset = (container - size) // 2
|
||||
return offset
|
||||
|
||||
size = region.size
|
||||
offset_x = align(grid_size.width, container.width, col_align)
|
||||
offset_y = align(grid_size.height, container.height, row_align)
|
||||
|
||||
region = region.translate(offset_x, offset_y)
|
||||
return region
|
||||
|
||||
def get_widgets(self) -> Iterable[Widget]:
|
||||
return self.widgets.keys()
|
||||
|
||||
def generate_map(
|
||||
self, width: int, height: int, offset: Point = Point(0, 0)
|
||||
) -> dict[Widget, OrderedRegion]:
|
||||
|
||||
@@ -110,6 +110,7 @@ class Page(Widget):
|
||||
self._page.update(renderable)
|
||||
else:
|
||||
self._page.clear()
|
||||
self.require_repaint()
|
||||
|
||||
@property
|
||||
def virtual_size(self) -> Dimensions:
|
||||
|
||||
@@ -96,7 +96,6 @@ class ScrollBarRender:
|
||||
blank = " " * width_thickness
|
||||
|
||||
foreground_meta = {"@mouse.up": "release", "@mouse.down": "grab"}
|
||||
|
||||
if window_size and size and virtual_size:
|
||||
step_size = virtual_size / size
|
||||
|
||||
@@ -143,6 +142,7 @@ class ScrollBarRender:
|
||||
def __rich_console__(
|
||||
self, console: Console, options: ConsoleOptions
|
||||
) -> RenderResult:
|
||||
log.debug("SCROLLBAR RENDER")
|
||||
size = (
|
||||
(options.height or console.height)
|
||||
if self.vertical
|
||||
@@ -254,4 +254,6 @@ if __name__ == "__main__":
|
||||
console = Console()
|
||||
bar = ScrollBarRender()
|
||||
|
||||
console.print(ScrollBarRender(position=15.3, thickness=5, vertical=False))
|
||||
console.print(
|
||||
ScrollBarRender(position=15.3, window_size=100, thickness=5, vertical=True)
|
||||
)
|
||||
|
||||
@@ -97,6 +97,10 @@ class View(Widget):
|
||||
hidden, shown, resized = self.layout.reflow(width, height)
|
||||
self.app.refresh()
|
||||
|
||||
for widget in self.layout.get_widgets():
|
||||
if not self.is_mounted(widget):
|
||||
await self.mount(widget)
|
||||
|
||||
for widget in hidden:
|
||||
widget.post_message_no_wait(events.Hide(self))
|
||||
for widget in shown:
|
||||
@@ -104,9 +108,8 @@ class View(Widget):
|
||||
|
||||
send_resize = shown
|
||||
send_resize.update(resized)
|
||||
|
||||
for widget, region in self.layout:
|
||||
if not self.is_mounted(widget):
|
||||
await self.mount(widget)
|
||||
if widget in send_resize:
|
||||
widget.post_message_no_wait(
|
||||
events.Resize(self, region.width, region.height)
|
||||
|
||||
@@ -66,7 +66,7 @@ class Widget(MessagePump):
|
||||
visible: Reactive[bool] = Reactive(True, layout=True)
|
||||
layout_size: Reactive[int | None] = Reactive(None)
|
||||
layout_fraction: Reactive[int] = Reactive(1)
|
||||
layout_minimim_size: Reactive[int] = Reactive(1)
|
||||
layout_min_size: Reactive[int] = Reactive(1)
|
||||
layout_offset_x: Reactive[float] = Reactive(0, layout=True)
|
||||
layout_offset_y: Reactive[float] = Reactive(0, layout=True)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from ._footer import Footer
|
||||
from ._header import Header
|
||||
from ._button import Button
|
||||
from ._placeholder import Placeholder
|
||||
from ._scroll_view import ScrollView
|
||||
from ._static import Static
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
from rich.console import Console, ConsoleOptions, RenderableType
|
||||
from rich.repr import rich_repr, RichReprResult
|
||||
from rich.console import RenderableType
|
||||
from rich.text import Text
|
||||
import rich.repr
|
||||
|
||||
from .. import events
|
||||
from ..widget import Widget
|
||||
|
||||
|
||||
@rich.repr.auto
|
||||
class Footer(Widget):
|
||||
def __init__(self) -> None:
|
||||
self.keys: list[tuple[str, str]] = []
|
||||
super().__init__()
|
||||
self.layout_size = 1
|
||||
|
||||
def __rich_repr__(self) -> RichReprResult:
|
||||
def __rich_repr__(self) -> rich.repr.RichReprResult:
|
||||
yield "footer"
|
||||
|
||||
def add_key(self, key: str, label: str) -> None:
|
||||
|
||||
@@ -7,7 +7,6 @@ from rich.style import StyleType
|
||||
|
||||
|
||||
from .. import events
|
||||
from ..geometry import Dimensions
|
||||
from ..message import Message
|
||||
from ..scrollbar import ScrollTo, ScrollBar
|
||||
from ..geometry import clamp
|
||||
|
||||
@@ -11,3 +11,7 @@ class Static(Widget):
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return self.renderable
|
||||
|
||||
async def update(self, renderable: RenderableType) -> None:
|
||||
self.renderable = renderable
|
||||
self.require_repaint()
|
||||
|
||||
Reference in New Issue
Block a user