Skip to content

Commit cd7ab61

Browse files
authored
🔀 Merge pull request #26 from davep/button-labels
Make dialog button labels configurable
2 parents 58bd86b + d9c3b60 commit cd7ab61

File tree

6 files changed

+90
-15
lines changed

6 files changed

+90
-15
lines changed

ChangeLog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# ChangeLog
22

3+
## Unreleased
4+
5+
**Released: WiP**
6+
7+
- Added the ability to configure any button label of any dialog.
8+
([#26](https://github.com/davep/textual-fspicker/pull/26))
9+
310
## v0.3.0
411

512
**Released: 2025-02-19**

src/textual_fspicker/base_dialog.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# Python imports.
99
import sys
1010
from pathlib import Path
11+
from typing import Callable, TypeAlias
1112

1213
##############################################################################
1314
# Textual imports.
@@ -33,6 +34,11 @@ class InputBar(Horizontal):
3334
"""The input bar area of the dialog."""
3435

3536

37+
##############################################################################
38+
ButtonLabel: TypeAlias = str | Callable[[str], str]
39+
"""The type for a button label value."""
40+
41+
3642
##############################################################################
3743
class FileSystemPickerScreen(ModalScreen[Path | None]):
3844
"""Base screen for the dialogs in this library."""
@@ -78,27 +84,49 @@ class FileSystemPickerScreen(ModalScreen[Path | None]):
7884
"""The bindings for the dialog."""
7985

8086
def __init__(
81-
self, location: str | Path = ".", title: str = "", select_button: str = ""
87+
self,
88+
location: str | Path = ".",
89+
title: str = "",
90+
select_button: ButtonLabel = "",
91+
cancel_button: ButtonLabel = "",
8292
) -> None:
8393
"""Initialise the dialog.
8494
8595
Args:
8696
location: Optional starting location.
8797
title: Optional title.
88-
select_button: Label for the select button.
98+
select_button: Label or format function for the select button.
99+
cancel_button: Label or format function for the cancel button.
89100
"""
90101
super().__init__()
91102
self._location = location
92103
"""The starting location."""
93104
self._title = title
94105
"""The title for the dialog."""
95-
self._select_button = select_button or "Select"
96-
"""The text prompt for the select button."""
106+
self._select_button = select_button
107+
"""The text prompt for the select button, or a function to format it."""
108+
self._cancel_button = cancel_button
109+
"""The text prompt for the cancel button, or a function to format it."""
97110

98111
def _input_bar(self) -> ComposeResult:
99112
"""Provide any widgets for the input bar, before the buttons."""
100113
yield from ()
101114

115+
@staticmethod
116+
def _label(label: ButtonLabel, default: str) -> str:
117+
"""Create a label for use with a button.
118+
119+
Args:
120+
label: The label value for the button.
121+
default: The default label for the button.
122+
123+
Returns:
124+
The formatted label.
125+
"""
126+
# If the label is callable, then call it with the default as a
127+
# parameter; otherwise use it as-is as it'll be a string.
128+
return label(default) if callable(label) else label or default
129+
102130
def compose(self) -> ComposeResult:
103131
"""Compose the child widgets.
104132
@@ -113,8 +141,8 @@ def compose(self) -> ComposeResult:
113141
yield DirectoryNavigation(self._location)
114142
with InputBar():
115143
yield from self._input_bar()
116-
yield Button(self._select_button, id="select")
117-
yield Button("Cancel", id="cancel")
144+
yield Button(self._label(self._select_button, "Select"), id="select")
145+
yield Button(self._label(self._cancel_button, "Cancel"), id="cancel")
118146

119147
def on_mount(self) -> None:
120148
"""Focus directory widget on mount."""

src/textual_fspicker/file_dialog.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
##############################################################################
2020
# Local imports.
21-
from .base_dialog import FileSystemPickerScreen
21+
from .base_dialog import ButtonLabel, FileSystemPickerScreen
2222
from .parts import DirectoryNavigation, DriveNavigation
2323
from .path_filters import Filters
2424
from .path_maker import MakePath
@@ -48,7 +48,8 @@ def __init__(
4848
self,
4949
location: str | Path = ".",
5050
title: str = "Open",
51-
select_button: str = "",
51+
select_button: ButtonLabel = "",
52+
cancel_button: ButtonLabel = "",
5253
*,
5354
filters: Filters | None = None,
5455
default_file: str | Path | None = None,
@@ -58,11 +59,14 @@ def __init__(
5859
Args:
5960
location: Optional starting location.
6061
title: Optional title.
61-
select_button: The label for the selection button.
62+
select_button: The label for the select button.
63+
cancel_button: The label for the cancel button.
6264
filters: Optional filters to show in the dialog.
6365
default_file: The default filename to place in the input.
6466
"""
65-
super().__init__(location, title, select_button=select_button)
67+
super().__init__(
68+
location, title, select_button=select_button, cancel_button=cancel_button
69+
)
6670
self._filters = filters
6771
"""The filters for the dialog."""
6872
self._default_file = default_file

src/textual_fspicker/file_open.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
##############################################################################
1212
# Local imports.
13+
from .base_dialog import ButtonLabel
1314
from .file_dialog import BaseFileDialog
1415
from .path_filters import Filters
1516

@@ -23,6 +24,8 @@ def __init__(
2324
location: str | Path = ".",
2425
title: str = "Open",
2526
*,
27+
open_button: ButtonLabel = "",
28+
cancel_button: ButtonLabel = "",
2629
filters: Filters | None = None,
2730
must_exist: bool = True,
2831
default_file: str | Path | None = None,
@@ -32,14 +35,22 @@ def __init__(
3235
Args:
3336
location: Optional starting location.
3437
title: Optional title.
38+
open_button: The label for the open button.
39+
cancel_button: The label for the cancel button.
3540
filters: Optional filters to show in the dialog.
3641
must_exist: Flag to say if the file must exist.
3742
default_file: The default filename to place in the input.
43+
44+
Notes:
45+
`open_button` and `cancel_button` can either be strings that
46+
set the button label, or they can be functions that take the
47+
default button label as a parameter and return the label to use.
3848
"""
3949
super().__init__(
4050
location,
4151
title,
42-
select_button="Open",
52+
select_button=self._label(open_button, "Open"),
53+
cancel_button=cancel_button,
4354
filters=filters,
4455
default_file=default_file,
4556
)

src/textual_fspicker/file_save.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
##############################################################################
1212
# Local imports.
13+
from .base_dialog import ButtonLabel
1314
from .file_dialog import BaseFileDialog
1415
from .path_filters import Filters
1516

@@ -23,6 +24,8 @@ def __init__(
2324
location: str | Path = ".",
2425
title: str = "Save as",
2526
*,
27+
save_button: ButtonLabel = "",
28+
cancel_button: ButtonLabel = "",
2629
filters: Filters | None = None,
2730
can_overwrite: bool = True,
2831
default_file: str | Path | None = None,
@@ -32,14 +35,22 @@ def __init__(
3235
Args:
3336
location: Optional starting location.
3437
title: Optional title.
38+
save_button: The label for the save button.
39+
cancel_button: The label for the cancel button.
3540
filters: Optional filters to show in the dialog.
3641
can_overwrite: Flag to say if an existing file can be overwritten.
3742
default_file: The default filename to place in the input.
43+
44+
Notes:
45+
`open_button` and `cancel_button` can either be strings that
46+
set the button label, or they can be functions that take the
47+
default button label as a parameter and return the label to use.
3848
"""
3949
super().__init__(
4050
location,
4151
title,
42-
select_button="Save",
52+
select_button=self._label(save_button, "Save"),
53+
cancel_button=cancel_button,
4354
filters=filters,
4455
default_file=default_file,
4556
)

src/textual_fspicker/select_directory.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
##############################################################################
1919
# Local imports.
20-
from .base_dialog import FileSystemPickerScreen
20+
from .base_dialog import ButtonLabel, FileSystemPickerScreen
2121
from .parts import DirectoryNavigation
2222

2323

@@ -60,15 +60,29 @@ class SelectDirectory(FileSystemPickerScreen):
6060
"""A directory selection dialog."""
6161

6262
def __init__(
63-
self, location: str | Path = ".", title: str = "Select directory"
63+
self,
64+
location: str | Path = ".",
65+
title: str = "Select directory",
66+
*,
67+
select_button: ButtonLabel = "",
68+
cancel_button: ButtonLabel = "",
6469
) -> None:
6570
"""Initialise the dialog.
6671
6772
Args:
6873
location: Optional starting location.
6974
title: Optional title.
75+
select_button: The label for the select button.
76+
cancel_button: The label for the cancel button.
77+
78+
Notes:
79+
`select_button` and `cancel_button` can either be strings that
80+
set the button label, or they can be functions that take the
81+
default button label as a parameter and return the label to use.
7082
"""
71-
super().__init__(location, title)
83+
super().__init__(
84+
location, title, select_button=select_button, cancel_button=cancel_button
85+
)
7286

7387
def on_mount(self) -> None:
7488
"""Configure the dialog once the DOM is ready."""

0 commit comments

Comments
 (0)