Skip to main content

A Sliding Container widget for Textual

Project description

textual-slidecontainer

textual-slidecontainer

This is a library that provides a custom container (widget) called the SlideContainer.

It is designed to make it extremely simple to implement a sliding menu bar in yor Textual apps.

Features

  • Usage is a single line of code with the default settings. Everything is handled automatically.
  • Set the direction - Containers can slide to the left, right, top, or bottom, independently of where they are on the screen.
  • Enable or disable Floating mode - With a simple boolean, containers can switch between floating on top of your app, or being a part of it and affecting the layout.
  • Set the default state - Containers can be set to start in closed mode.
  • Set the container to dock as an initialization argument.
  • Floating containers automatically dock to the edge they move towards (this can be changed).
  • Change how the animation looks with the duration, fade, and easing_function arguments.
  • Included demo application which has comments in the code.

Installation

Install with:

pip install textual-slidecontainer

or for uv users:

uv add textual-slidecontainer

Import into your project with:

from textual_slidecontainer import SlideContainer

Demo app

You can instantly try out the demo app using uv or pipx:

uvx textual-slidecontainer
pipx textual-slidecontainer

Or if you have it downloaded into your python environment, run it using the entry script:

textual-slidecontainer

For uv users:

uv run textual-slidecontainer

Usage

Most basic usage

from textual_slidecontainer import SlideContainer

def compose(self):
    with SlideContainer(id = "my_slidecontainer", slide_direction = "up"):
        yield Static("Your widgets here")

Set the container's width and height in CSS as you usually would. Note that the above example will dock to the top of your screen automatically because it is in floating mode (floating is the default).

Start closed / Hidden

If you'd like the container to start closed/hidden, simply set start_open to False:

def compose(self):
    with SlideContainer(
        id = "my_slidecontainer", slide_direction = "left", start_open = False      
    ):
        yield Static("Your widgets here")

Different slide and dock directions

You can set the slide direction and dock direction to be different:

def compose(self):
    with SlideContainer(
        id = "my_slidecontainer", slide_direction = "right", dock_direction = "top"       
    ):
        yield Static("Your widgets here")

All Arguments

Here's an example using all the arguments:

with SlideContainer(
    classes = "my_container_classes",
    id = "my_slidecontainer",
    start_open = False,         
    slide_direction = "left",
    dock_direction = "top",      # dock to the top but slide left
    floating = False,            # default is True
    fade = True,
    duration = 0.6,                   # the default is 0.8     
    easing_function = "out_bounce"    # default is "out_cubic".                           
):
    yield Static("Your widgets here")

Ways of Making the Container

You can also use the other Textual methods of using a container: passing in a list of children, or making a custom class that inherits from SlideContainer.

Passing in a list of children (the normal Textual syntax):

window_widgets: list[Widget] = [
    Button("Label", id="button_foo"),
    Static("Your widgets here")
]
yield SlideContainer(*window_widgets)

You can just as easily define everything you want in a custom container (again, the normal Textual syntax):

class MySlideContainer(SlideContainer):

    def __init__(self):
        super().__init__(
            slide_direction="top",
            start_open=False,
            id="your_slidecontainer"
        )

    def compose(self):
        yield Button("Label", id="button_foo")
        yield Static("Your widgets here")

Full demonstration

Here's a full demonstration of it being used in a small app. You can copy and paste this code.

from textual.app import App
from textual import on
from textual.widgets import Static, Footer, Button
from textual.containers import Container

from textual_slidecontainer import SlideContainer

class TextualApp(App):

    DEFAULT_CSS = """
    #my_container {
        width: 1fr; height: 1fr; border: solid red;
        align: center middle; content-align: center middle;
    }
    #my_static { border: solid blue; width: 1fr;}
    SlideContainer {
        width: 25; height: 1fr;
        background: $panel; align: center middle;
    }
    """
    def compose(self):

        # The container will start closed / hidden:
        with SlideContainer(slide_direction="left", start_open=False):
            yield Static("This is content in the slide container", id="my_static")
        with Container(id="my_container"):
            yield Button("Show/Hide slide container", id="toggle_slide")
        yield Footer()

    @on(Button.Pressed, "#toggle_slide")
    def toggle_slide(self) -> None:
        self.query_one(SlideContainer).toggle()

TextualApp().run()

Check out the source code of the demo app to see a more in-depth example.

Messages

The SlideContainer posts two messages:

  • SlideCompleted
  • InitClosed

SlideCompleted

This message will be posted every time that a slide is completed. This is useful if you need something to refresh every time the container slides open or closed (ie. refreshing elements on your screen affected by layout changes, or something inside the SlideContainer itself). It contains two attributes:

  • state: bool - Whether it just slid open or closed.
    True = open, False = closed.
  • container - The container that did the sliding.

Example usage:

from textual import on

@on(SlideContainer.SlideCompleted, "#my_container")    # Note the selector is optional.
def my_slide_completed(self, event: SlideContainer.SlideCompleted):

    self.notify(f"Slide completed: {event.container}: {event.state}")

# OR using the other method:
def on_slide_container_slide_completed(self, event: SlideContainer.SlideCompleted):
    # handle your loading screen here.

InitClosed

Because the container needs to know where it should be on the screen in open mode, starting in closed mode can sometimes reveal some graphical glitches that are tricky to deal with. In order to help solve this problem, the container provides an InitClosed message. This is only posted after the container has been mounted and moved to its closed position. It contains one attribute:

  • container - The container that just initialized in the closed position.
from textual import on

@on(SlideContainer.InitClosed, "#my_container")    # Note the selector is optional.
def my_container_loaded(self, event: SlideContainer.InitClosed):
    self.log(f"Slide container initialized closed: {event.container}")
    # However you want to deal with your loading logic  here.

# OR using the other method:
def on_slide_container_init_closed(self, event: SlideContainer.InitClosed):
    # handle your loading logic  here.

You can see an example of this being used in the demo app.

Questions, issues, suggestions?

Feel free to post an issue.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

textual_slidecontainer-0.4.0.tar.gz (147.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

textual_slidecontainer-0.4.0-py3-none-any.whl (10.7 kB view details)

Uploaded Python 3

File details

Details for the file textual_slidecontainer-0.4.0.tar.gz.

File metadata

File hashes

Hashes for textual_slidecontainer-0.4.0.tar.gz
Algorithm Hash digest
SHA256 d572e1d700e44d24f0f82fcccf990540acc81f8c751f05c93f1f6354b2007e21
MD5 262e20e36e3a1409a7c7b245e1a72bdc
BLAKE2b-256 1c000bff4f5839208d31c871ad9d045c46451071e4844b16c6d7f442db8fb60a

See more details on using hashes here.

File details

Details for the file textual_slidecontainer-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for textual_slidecontainer-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 35f58ae680b760cb7bea0325d9c73ed73d76f66da5fc8479b56a34f4c0333adc
MD5 15b3964d8dd31cb48389468ce12a491b
BLAKE2b-256 91e7693f5068bec0f9c1dedb5fda18913e48987d9008dba1d0e28e822a76b27a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page