tui: Q&D adaptation to new code:
use the new `quick_bridge` adapter and do few changes to make TUI work with new backend. TUI will probably be rewritten at some point, this is just an update to keep it working. `urwid` has been updated to have a version working with Python 3.14.
This commit is contained in:
parent
d3dc59b141
commit
e5ed4e3d7c
7 changed files with 135 additions and 77 deletions
|
|
@ -28,9 +28,10 @@ from libervia.backend.core import log as logging
|
|||
log = logging.getLogger(__name__)
|
||||
from libervia.backend.tools import config as tools_config
|
||||
import urwid
|
||||
from urwid.util import is_wide_char
|
||||
from urwid import is_wide_char
|
||||
from urwid_satext import sat_widgets
|
||||
from libervia.frontends.quick_frontend.quick_app import QuickApp
|
||||
from libervia.frontends.quick_frontend import quick_bridge
|
||||
from libervia.frontends.quick_frontend import quick_utils
|
||||
from libervia.frontends.quick_frontend import quick_chat
|
||||
from libervia.tui.profile_manager import ProfileManager
|
||||
|
|
@ -45,6 +46,7 @@ from libervia.tui.keys import action_key_map as a_key
|
|||
from libervia.frontends.tools.misc import InputHistory
|
||||
from libervia.backend.tools.common import dynamic_import
|
||||
from libervia.frontends.tools import jid
|
||||
import asyncio
|
||||
import signal
|
||||
import sys
|
||||
|
||||
|
|
@ -67,10 +69,19 @@ class TUISettings(tools_config.FrontendSettings, key="tui"):
|
|||
## bridge handling
|
||||
# we get bridge name from conf and initialise the right class accordingly
|
||||
main_config = tools_config.parse_main_conf(ignore_cache=True)
|
||||
bridge_name = next(iter(main_config.common.bridges)).type
|
||||
bridge_name = next(iter(main_config.bridges)).type
|
||||
if "dbus" not in bridge_name:
|
||||
print("only D-Bus bridge is currently supported")
|
||||
sys.exit(3)
|
||||
# install the asyncio twisted reactor
|
||||
try:
|
||||
asyncio.get_event_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
from twisted.internet import asyncioreactor
|
||||
|
||||
asyncioreactor.install()
|
||||
|
||||
|
||||
class EditBar(sat_widgets.ModalEdit):
|
||||
|
|
@ -338,9 +349,7 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
AVATARS_HANDLER = False
|
||||
|
||||
def __init__(self):
|
||||
bridge_module = dynamic_import.import_bridge(
|
||||
bridge_name, "libervia.frontends.bridge"
|
||||
)
|
||||
bridge_module = dynamic_import.import_bridge(bridge_name)
|
||||
if bridge_module is None:
|
||||
log.error("Can't import {} bridge".format(bridge_name))
|
||||
sys.exit(3)
|
||||
|
|
@ -348,16 +357,18 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
log.debug("Loading {} bridge".format(bridge_name))
|
||||
QuickApp.__init__(
|
||||
self,
|
||||
bridge_factory=bridge_module.bridge,
|
||||
# AIOBridge is the only bridge compatible with the asyncio loop
|
||||
bridge_factory=lambda: quick_bridge.QuickBridgeAdapter(
|
||||
bridge_module.AIOBridge()
|
||||
),
|
||||
xmlui=xmlui,
|
||||
check_options=quick_utils.check_options,
|
||||
connect_bridge=False,
|
||||
)
|
||||
self.tui_settings = main_config.get_settings(TUISettings)
|
||||
## main loop setup ##
|
||||
event_loop = (
|
||||
urwid.GLibEventLoop if "dbus" in bridge_name else urwid.TwistedEventLoop
|
||||
)
|
||||
# the new D-Bus bridge is twisted based (txdbus)
|
||||
event_loop = urwid.TwistedEventLoop
|
||||
self.loop = urwid.MainLoop(
|
||||
urwid.SolidFill(),
|
||||
C.PALETTE,
|
||||
|
|
@ -373,6 +384,10 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
def on_bridge_connected(self):
|
||||
|
||||
##misc setup##
|
||||
self._version = None
|
||||
self.bridge.version_get(
|
||||
callback=self._version_get_cb, errback=lambda __: None
|
||||
)
|
||||
self._visible_widgets = set()
|
||||
self.notif_bar = sat_widgets.NotificationBar()
|
||||
urwid.connect_signal(self.notif_bar, "change", self.on_notification)
|
||||
|
|
@ -400,6 +415,9 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
self.loop.widget = self.main_widget = ProfileManager(self)
|
||||
self.post_init()
|
||||
|
||||
def _version_get_cb(self, version):
|
||||
self._version = version
|
||||
|
||||
@property
|
||||
def visible_widgets(self):
|
||||
return self._visible_widgets
|
||||
|
|
@ -579,7 +597,7 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
self.__saved_overlay = None
|
||||
|
||||
elif (
|
||||
input_ == a_key["DEBUG"] and ".dev0" in self.bridge.version_get()
|
||||
input_ == a_key["DEBUG"] and ".dev0" in (self._version or "")
|
||||
): # Debug only for dev versions
|
||||
self.debug()
|
||||
elif input_ == a_key["CONTACTS_HIDE"]: # user wants to (un)hide the contact lists
|
||||
|
|
@ -614,6 +632,9 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
|
||||
def add_menus(self, menu, type_filter, menu_data=None):
|
||||
"""Add cached menus to instance
|
||||
|
||||
The menus are retrieved asynchronously from the bridge.
|
||||
|
||||
@param menu: sat_widgets.Menu instance
|
||||
@param type_filter: menu type like is sat.core.sat_main.import_menu
|
||||
@param menu_data: data to send with these menus
|
||||
|
|
@ -623,16 +644,24 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
def add_menu_cb(callback_id):
|
||||
self.action_launch(callback_id, menu_data, profile=self.current_profile)
|
||||
|
||||
for id_, type_, path, path_i18n, extra in self.bridge.menus_get(
|
||||
"", C.NO_SECURITY_LIMIT
|
||||
): # TODO: manage extra
|
||||
if type_ != type_filter:
|
||||
continue
|
||||
if len(path) != 2:
|
||||
raise NotImplementedError("Menu with a path != 2 are not implemented yet")
|
||||
menu.add_menu(
|
||||
path_i18n[0], path_i18n[1], lambda dummy, id_=id_: add_menu_cb(id_)
|
||||
)
|
||||
def menus_get_cb(menus):
|
||||
for id_, type_, path, path_i18n, extra in menus: # TODO: manage extra
|
||||
if type_ != type_filter:
|
||||
continue
|
||||
if len(path) != 2:
|
||||
raise NotImplementedError(
|
||||
"Menu with a path != 2 are not implemented yet"
|
||||
)
|
||||
menu.add_menu(
|
||||
path_i18n[0], path_i18n[1], lambda dummy, id_=id_: add_menu_cb(id_)
|
||||
)
|
||||
|
||||
def menus_get_eb(failure):
|
||||
log.error("Can't get menus: {}".format(failure))
|
||||
|
||||
self.bridge.menus_get(
|
||||
"", C.NO_SECURITY_LIMIT, callback=menus_get_cb, errback=menus_get_eb
|
||||
)
|
||||
|
||||
def _build_menu_roller(self):
|
||||
menu = sat_widgets.Menu(self.loop)
|
||||
|
|
@ -1016,19 +1045,25 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
|||
|
||||
def on_join_room_request(self, menu):
|
||||
"""User wants to join a MUC room"""
|
||||
pop_up_widget = sat_widgets.InputDialog(
|
||||
_("Entering a MUC room"),
|
||||
_("Please enter MUC's JID"),
|
||||
default_txt=self.bridge.muc_get_default_service(),
|
||||
ok_cb=self.on_join_room,
|
||||
|
||||
def show_join_dialog(default_service):
|
||||
pop_up_widget = sat_widgets.InputDialog(
|
||||
_("Entering a MUC room"),
|
||||
_("Please enter MUC's JID"),
|
||||
default_txt=default_service,
|
||||
ok_cb=self.on_join_room,
|
||||
)
|
||||
pop_up_widget.set_callback(
|
||||
"cancel", lambda dummy: self.remove_pop_up(pop_up_widget)
|
||||
)
|
||||
self.show_pop_up(pop_up_widget)
|
||||
|
||||
self.bridge.muc_get_default_service(
|
||||
callback=show_join_dialog, errback=lambda __: show_join_dialog("")
|
||||
)
|
||||
pop_up_widget.set_callback(
|
||||
"cancel", lambda dummy: self.remove_pop_up(pop_up_widget)
|
||||
)
|
||||
self.show_pop_up(pop_up_widget)
|
||||
|
||||
def on_about_request(self, menu):
|
||||
self.alert(_("About"), C.APP_NAME + " v" + self.bridge.version_get())
|
||||
self.alert(_("About"), C.APP_NAME + " v" + (self._version or ""))
|
||||
|
||||
# MISC CALLBACKS#
|
||||
|
||||
|
|
|
|||
|
|
@ -174,10 +174,13 @@ class Card(TarotCard):
|
|||
return CardDisplayer(self)
|
||||
|
||||
|
||||
class Table(urwid.FlowWidget):
|
||||
class Table(urwid.Widget):
|
||||
"""Represent the cards currently on the table"""
|
||||
|
||||
_sizing = frozenset([urwid.Sizing.FLOW])
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.top = self.left = self.bottom = self.right = None
|
||||
|
||||
def put_card(self, location, card):
|
||||
|
|
@ -313,7 +316,7 @@ class TarotGame(QuickTarotGame, urwid.WidgetWrap):
|
|||
if not winners and not loosers:
|
||||
title = _("Draw game")
|
||||
else:
|
||||
title = _("You win \o/") if self.player_nick in winners else _("You loose :(")
|
||||
title = _("You win \\o/") if self.player_nick in winners else _("You loose :(")
|
||||
form = xmlui.create(
|
||||
self.parent.host,
|
||||
xml_data,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dbus
|
||||
try:
|
||||
import dbus
|
||||
except ImportError:
|
||||
dbus = None
|
||||
|
||||
|
||||
class Notify(object):
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ class ProfileManager(QuickProfileManager, urwid.WidgetWrap):
|
|||
self.pass_wid = sat_widgets.Password(_("Password:"), align="center")
|
||||
|
||||
style = ["no_first_select"]
|
||||
profiles = host.bridge.profiles_list_get()
|
||||
profiles.sort()
|
||||
self.list_profile = sat_widgets.List(
|
||||
profiles, style=style, align="center", on_change=self.on_profile_change
|
||||
[], style=style, align="center", on_change=self.on_profile_change
|
||||
)
|
||||
# the profiles list is retrieved asynchronously
|
||||
self.refill_profiles()
|
||||
|
||||
# new & delete buttons
|
||||
buttons = [
|
||||
|
|
|
|||
|
|
@ -54,24 +54,31 @@ class Progress(urwid.WidgetWrap, quick_widgets.QuickWidget):
|
|||
|
||||
def progress_cb(self, loop, data):
|
||||
progress_id, message, profile = data
|
||||
data = self.host.bridge.progress_get(progress_id, profile)
|
||||
pbar = self.progress_dict[(progress_id, profile)]["progress"]
|
||||
if data:
|
||||
if self.progress_dict[(progress_id, profile)]["state"] == "init":
|
||||
# first answer, we must construct the bar
|
||||
self.progress_dict[(progress_id, profile)]["state"] = "progress"
|
||||
pbar.done = float(data["size"])
|
||||
|
||||
pbar.set_completion(float(data["position"]))
|
||||
self.update_not_bar()
|
||||
else:
|
||||
if self.progress_dict[(progress_id, profile)]["state"] == "progress":
|
||||
self.progress_dict[(progress_id, profile)]["state"] = "done"
|
||||
pbar.set_completion(pbar.done)
|
||||
self.update_not_bar()
|
||||
def progress_get_cb(data):
|
||||
entry = self.progress_dict.get((progress_id, profile))
|
||||
if entry is None:
|
||||
# the bar has been removed in the meantime
|
||||
return
|
||||
pbar = entry["progress"]
|
||||
if data:
|
||||
if entry["state"] == "init":
|
||||
# first answer, we must construct the bar
|
||||
entry["state"] = "progress"
|
||||
pbar.done = float(data["size"])
|
||||
|
||||
loop.set_alarm_in(0.2, self.progress_cb, (progress_id, message, profile))
|
||||
pbar.set_completion(float(data["position"]))
|
||||
self.update_not_bar()
|
||||
else:
|
||||
if entry["state"] == "progress":
|
||||
entry["state"] = "done"
|
||||
pbar.set_completion(pbar.done)
|
||||
self.update_not_bar()
|
||||
return
|
||||
|
||||
loop.set_alarm_in(0.2, self.progress_cb, (progress_id, message, profile))
|
||||
|
||||
self.host.bridge.progress_get(progress_id, profile, callback=progress_get_cb)
|
||||
|
||||
def _remove_bar(self, progress_id, profile):
|
||||
wid = self.progress_dict[(progress_id, profile)]["full"]
|
||||
|
|
|
|||
|
|
@ -45,34 +45,44 @@ class StatusBar(urwid.Columns):
|
|||
urwid.connect_signal(self.status, "click", self.on_status_click)
|
||||
|
||||
def on_presence_click(self, sender=None):
|
||||
if not self.host.bridge.is_connected(
|
||||
self.host.current_profile
|
||||
): # FIXME: manage multi-profiles
|
||||
return
|
||||
options = [commonConst.PRESENCE[presence] for presence in commonConst.PRESENCE]
|
||||
list_widget = sat_widgets.GenericList(
|
||||
options=options,
|
||||
option_type=sat_widgets.ClickableText,
|
||||
on_click=self.on_change,
|
||||
def show_presence_popup(connected):
|
||||
if not connected:
|
||||
return
|
||||
options = [
|
||||
commonConst.PRESENCE[presence] for presence in commonConst.PRESENCE
|
||||
]
|
||||
list_widget = sat_widgets.GenericList(
|
||||
options=options,
|
||||
option_type=sat_widgets.ClickableText,
|
||||
on_click=self.on_change,
|
||||
)
|
||||
decorated = sat_widgets.LabelLine(
|
||||
list_widget, sat_widgets.SurroundedText(_("Set your presence"))
|
||||
)
|
||||
self.host.show_pop_up(decorated)
|
||||
|
||||
# FIXME: manage multi-profiles
|
||||
self.host.bridge.is_connected(
|
||||
self.host.current_profile, callback=show_presence_popup
|
||||
)
|
||||
decorated = sat_widgets.LabelLine(
|
||||
list_widget, sat_widgets.SurroundedText(_("Set your presence"))
|
||||
)
|
||||
self.host.show_pop_up(decorated)
|
||||
|
||||
def on_status_click(self, sender=None):
|
||||
if not self.host.bridge.is_connected(
|
||||
self.host.current_profile
|
||||
): # FIXME: manage multi-profiles
|
||||
return
|
||||
pop_up_widget = sat_widgets.InputDialog(
|
||||
_("Set your status"),
|
||||
_("New status"),
|
||||
default_txt=self.status.get_text(),
|
||||
cancel_cb=lambda _: self.host.remove_pop_up(),
|
||||
ok_cb=self.on_change,
|
||||
def show_status_popup(connected):
|
||||
if not connected:
|
||||
return
|
||||
pop_up_widget = sat_widgets.InputDialog(
|
||||
_("Set your status"),
|
||||
_("New status"),
|
||||
default_txt=self.status.get_text(),
|
||||
cancel_cb=lambda _: self.host.remove_pop_up(),
|
||||
ok_cb=self.on_change,
|
||||
)
|
||||
self.host.show_pop_up(pop_up_widget)
|
||||
|
||||
# FIXME: manage multi-profiles
|
||||
self.host.bridge.is_connected(
|
||||
self.host.current_profile, callback=show_status_popup
|
||||
)
|
||||
self.host.show_pop_up(pop_up_widget)
|
||||
|
||||
def on_change(self, sender=None, user_data=None):
|
||||
new_value = user_data.get_text()
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ dependencies = [
|
|||
"twisted[tls] >= 22.10",
|
||||
"twomemo >= 1.0.0, < 2",
|
||||
"txdbus",
|
||||
"urwid >= 1.2.0, < 3",
|
||||
"urwid >= 4.2.5, < 5",
|
||||
"urwid-satext",
|
||||
"xmlschema < 4.3.1",
|
||||
"tx-xmpp>=0.15",
|
||||
|
|
|
|||
Loading…
Reference in a new issue