from __future__ import annotations
import json
from collections.abc import Callable
from typing import Any
from ..base import Widget
[docs]
class MessageDialog(Widget):
"""A message box overlay (info/warning/error/question).
Shows an HTML overlay within the app window.
Pass ``callback`` to receive the clicked button label.
"""
def __init__(
self,
parent: Widget | None = None,
title: str = "",
text: str = "",
buttons: list[str] | None = None,
callback: Callable[[str], None] | None = None,
**kwargs: Any,
) -> None:
super().__init__(parent, **kwargs)
self._config_dict["_dialog_title"] = title
self._config_dict["_dialog_text"] = text
self._config_dict["_dialog_buttons"] = buttons or ["OK"]
self._dialog_callback = callback
[docs]
def show(self, app: Any = None) -> None:
a = app or self.app
if not a:
return
html = json.dumps(self._render())
a._eval_js(
f"(function(){{"
f'var e=document.getElementById("{self._id}-ov");'
f"if(!e){{"
f'document.body.insertAdjacentHTML("beforeend",{html});'
f"}}"
f'window.location.hash="{self._id}-ov";'
f"}})()"
)
@staticmethod
def _escape(text: str) -> str:
return (
text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace('"', """)
)
def _render(self) -> str:
title = self._escape(self._config_dict.get("_dialog_title", ""))
text = self._escape(self._config_dict.get("_dialog_text", ""))
buttons = self._config_dict.get("_dialog_buttons", ["OK"])
bid = self._id
btns_html = "".join(
f"<button class=\"iskg-btn\" onclick=\"iskg_bridge_event('{bid}','result',{json.dumps(b)})\">{self._escape(b)}</button>"
for b in buttons
)
return f'''<div id="{self._id}-ov" class="iskg-msgbox-overlay">
<div class="iskg-msgbox">
<div class="iskg-msgbox-title">{title}</div>
<div class="iskg-msgbox-text">{text}</div>
<div class="iskg-msgbox-btns">{btns_html}</div>
</div>
</div>'''
def _handle_bridge_event(self, event_name: str, event_data: Any) -> None:
if event_name == "result" and self._dialog_callback:
self._dialog_callback(str(event_data))
self._dialog_callback = None
super()._handle_bridge_event(event_name, event_data)
[docs]
def showinfo(
title: str = "",
text: str = "",
parent: Widget | None = None,
callback: Callable[[str], None] | None = None,
) -> MessageDialog:
"""Show an info message box (HTML overlay)."""
md = MessageDialog(parent=parent, title=title, text=text, buttons=["OK"], callback=callback)
return md
[docs]
def showwarning(
title: str = "",
text: str = "",
parent: Widget | None = None,
callback: Callable[[str], None] | None = None,
) -> MessageDialog:
"""Show a warning message box (HTML overlay)."""
md = MessageDialog(parent=parent, title=title, text=text, buttons=["OK"], callback=callback)
return md
[docs]
def showerror(
title: str = "",
text: str = "",
parent: Widget | None = None,
callback: Callable[[str], None] | None = None,
) -> MessageDialog:
"""Show an error message box (HTML overlay)."""
md = MessageDialog(parent=parent, title=title, text=text, buttons=["OK"], callback=callback)
return md
[docs]
def showquestion(
title: str = "",
text: str = "",
parent: Widget | None = None,
callback: Callable[[str], None] | None = None,
) -> MessageDialog:
"""Show a question dialog with Yes/No buttons (HTML overlay)."""
md = MessageDialog(
parent=parent, title=title, text=text, buttons=["Yes", "No"], callback=callback
)
return md
[docs]
class FileDialog:
"""Static methods for native file open/save dialogs."""
[docs]
@staticmethod
def open_file(
app: Any,
title: str = "Open",
directory: str = "",
file_types: list[str] | None = None,
multiple: bool = False,
) -> Any:
return app.file_dialog("open", directory, file_types, multiple)
[docs]
@staticmethod
def save_file(
app: Any,
title: str = "Save As",
directory: str = "",
file_types: list[str] | None = None,
) -> Any:
return app.file_dialog("save", directory, file_types, False)
[docs]
@staticmethod
def open_folder(
app: Any,
title: str = "Select Folder",
directory: str = "",
) -> Any:
return app.file_dialog("folder", directory, None, False)