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__)
|
log = logging.getLogger(__name__)
|
||||||
from libervia.backend.tools import config as tools_config
|
from libervia.backend.tools import config as tools_config
|
||||||
import urwid
|
import urwid
|
||||||
from urwid.util import is_wide_char
|
from urwid import is_wide_char
|
||||||
from urwid_satext import sat_widgets
|
from urwid_satext import sat_widgets
|
||||||
from libervia.frontends.quick_frontend.quick_app import QuickApp
|
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_utils
|
||||||
from libervia.frontends.quick_frontend import quick_chat
|
from libervia.frontends.quick_frontend import quick_chat
|
||||||
from libervia.tui.profile_manager import ProfileManager
|
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.frontends.tools.misc import InputHistory
|
||||||
from libervia.backend.tools.common import dynamic_import
|
from libervia.backend.tools.common import dynamic_import
|
||||||
from libervia.frontends.tools import jid
|
from libervia.frontends.tools import jid
|
||||||
|
import asyncio
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
@ -67,10 +69,19 @@ class TUISettings(tools_config.FrontendSettings, key="tui"):
|
||||||
## bridge handling
|
## bridge handling
|
||||||
# we get bridge name from conf and initialise the right class accordingly
|
# we get bridge name from conf and initialise the right class accordingly
|
||||||
main_config = tools_config.parse_main_conf(ignore_cache=True)
|
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:
|
if "dbus" not in bridge_name:
|
||||||
print("only D-Bus bridge is currently supported")
|
print("only D-Bus bridge is currently supported")
|
||||||
sys.exit(3)
|
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):
|
class EditBar(sat_widgets.ModalEdit):
|
||||||
|
|
@ -338,9 +349,7 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
AVATARS_HANDLER = False
|
AVATARS_HANDLER = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
bridge_module = dynamic_import.import_bridge(
|
bridge_module = dynamic_import.import_bridge(bridge_name)
|
||||||
bridge_name, "libervia.frontends.bridge"
|
|
||||||
)
|
|
||||||
if bridge_module is None:
|
if bridge_module is None:
|
||||||
log.error("Can't import {} bridge".format(bridge_name))
|
log.error("Can't import {} bridge".format(bridge_name))
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
@ -348,16 +357,18 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
log.debug("Loading {} bridge".format(bridge_name))
|
log.debug("Loading {} bridge".format(bridge_name))
|
||||||
QuickApp.__init__(
|
QuickApp.__init__(
|
||||||
self,
|
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,
|
xmlui=xmlui,
|
||||||
check_options=quick_utils.check_options,
|
check_options=quick_utils.check_options,
|
||||||
connect_bridge=False,
|
connect_bridge=False,
|
||||||
)
|
)
|
||||||
self.tui_settings = main_config.get_settings(TUISettings)
|
self.tui_settings = main_config.get_settings(TUISettings)
|
||||||
## main loop setup ##
|
## main loop setup ##
|
||||||
event_loop = (
|
# the new D-Bus bridge is twisted based (txdbus)
|
||||||
urwid.GLibEventLoop if "dbus" in bridge_name else urwid.TwistedEventLoop
|
event_loop = urwid.TwistedEventLoop
|
||||||
)
|
|
||||||
self.loop = urwid.MainLoop(
|
self.loop = urwid.MainLoop(
|
||||||
urwid.SolidFill(),
|
urwid.SolidFill(),
|
||||||
C.PALETTE,
|
C.PALETTE,
|
||||||
|
|
@ -373,6 +384,10 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
def on_bridge_connected(self):
|
def on_bridge_connected(self):
|
||||||
|
|
||||||
##misc setup##
|
##misc setup##
|
||||||
|
self._version = None
|
||||||
|
self.bridge.version_get(
|
||||||
|
callback=self._version_get_cb, errback=lambda __: None
|
||||||
|
)
|
||||||
self._visible_widgets = set()
|
self._visible_widgets = set()
|
||||||
self.notif_bar = sat_widgets.NotificationBar()
|
self.notif_bar = sat_widgets.NotificationBar()
|
||||||
urwid.connect_signal(self.notif_bar, "change", self.on_notification)
|
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.loop.widget = self.main_widget = ProfileManager(self)
|
||||||
self.post_init()
|
self.post_init()
|
||||||
|
|
||||||
|
def _version_get_cb(self, version):
|
||||||
|
self._version = version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def visible_widgets(self):
|
def visible_widgets(self):
|
||||||
return self._visible_widgets
|
return self._visible_widgets
|
||||||
|
|
@ -579,7 +597,7 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
self.__saved_overlay = None
|
self.__saved_overlay = None
|
||||||
|
|
||||||
elif (
|
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
|
): # Debug only for dev versions
|
||||||
self.debug()
|
self.debug()
|
||||||
elif input_ == a_key["CONTACTS_HIDE"]: # user wants to (un)hide the contact lists
|
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):
|
def add_menus(self, menu, type_filter, menu_data=None):
|
||||||
"""Add cached menus to instance
|
"""Add cached menus to instance
|
||||||
|
|
||||||
|
The menus are retrieved asynchronously from the bridge.
|
||||||
|
|
||||||
@param menu: sat_widgets.Menu instance
|
@param menu: sat_widgets.Menu instance
|
||||||
@param type_filter: menu type like is sat.core.sat_main.import_menu
|
@param type_filter: menu type like is sat.core.sat_main.import_menu
|
||||||
@param menu_data: data to send with these menus
|
@param menu_data: data to send with these menus
|
||||||
|
|
@ -623,17 +644,25 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
def add_menu_cb(callback_id):
|
def add_menu_cb(callback_id):
|
||||||
self.action_launch(callback_id, menu_data, profile=self.current_profile)
|
self.action_launch(callback_id, menu_data, profile=self.current_profile)
|
||||||
|
|
||||||
for id_, type_, path, path_i18n, extra in self.bridge.menus_get(
|
def menus_get_cb(menus):
|
||||||
"", C.NO_SECURITY_LIMIT
|
for id_, type_, path, path_i18n, extra in menus: # TODO: manage extra
|
||||||
): # TODO: manage extra
|
|
||||||
if type_ != type_filter:
|
if type_ != type_filter:
|
||||||
continue
|
continue
|
||||||
if len(path) != 2:
|
if len(path) != 2:
|
||||||
raise NotImplementedError("Menu with a path != 2 are not implemented yet")
|
raise NotImplementedError(
|
||||||
|
"Menu with a path != 2 are not implemented yet"
|
||||||
|
)
|
||||||
menu.add_menu(
|
menu.add_menu(
|
||||||
path_i18n[0], path_i18n[1], lambda dummy, id_=id_: add_menu_cb(id_)
|
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):
|
def _build_menu_roller(self):
|
||||||
menu = sat_widgets.Menu(self.loop)
|
menu = sat_widgets.Menu(self.loop)
|
||||||
general = _("General")
|
general = _("General")
|
||||||
|
|
@ -1016,10 +1045,12 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
|
|
||||||
def on_join_room_request(self, menu):
|
def on_join_room_request(self, menu):
|
||||||
"""User wants to join a MUC room"""
|
"""User wants to join a MUC room"""
|
||||||
|
|
||||||
|
def show_join_dialog(default_service):
|
||||||
pop_up_widget = sat_widgets.InputDialog(
|
pop_up_widget = sat_widgets.InputDialog(
|
||||||
_("Entering a MUC room"),
|
_("Entering a MUC room"),
|
||||||
_("Please enter MUC's JID"),
|
_("Please enter MUC's JID"),
|
||||||
default_txt=self.bridge.muc_get_default_service(),
|
default_txt=default_service,
|
||||||
ok_cb=self.on_join_room,
|
ok_cb=self.on_join_room,
|
||||||
)
|
)
|
||||||
pop_up_widget.set_callback(
|
pop_up_widget.set_callback(
|
||||||
|
|
@ -1027,8 +1058,12 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
)
|
)
|
||||||
self.show_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("")
|
||||||
|
)
|
||||||
|
|
||||||
def on_about_request(self, menu):
|
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#
|
# MISC CALLBACKS#
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,10 +174,13 @@ class Card(TarotCard):
|
||||||
return CardDisplayer(self)
|
return CardDisplayer(self)
|
||||||
|
|
||||||
|
|
||||||
class Table(urwid.FlowWidget):
|
class Table(urwid.Widget):
|
||||||
"""Represent the cards currently on the table"""
|
"""Represent the cards currently on the table"""
|
||||||
|
|
||||||
|
_sizing = frozenset([urwid.Sizing.FLOW])
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
self.top = self.left = self.bottom = self.right = None
|
self.top = self.left = self.bottom = self.right = None
|
||||||
|
|
||||||
def put_card(self, location, card):
|
def put_card(self, location, card):
|
||||||
|
|
@ -313,7 +316,7 @@ class TarotGame(QuickTarotGame, urwid.WidgetWrap):
|
||||||
if not winners and not loosers:
|
if not winners and not loosers:
|
||||||
title = _("Draw game")
|
title = _("Draw game")
|
||||||
else:
|
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(
|
form = xmlui.create(
|
||||||
self.parent.host,
|
self.parent.host,
|
||||||
xml_data,
|
xml_data,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
try:
|
||||||
import dbus
|
import dbus
|
||||||
|
except ImportError:
|
||||||
|
dbus = None
|
||||||
|
|
||||||
|
|
||||||
class Notify(object):
|
class Notify(object):
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,11 @@ class ProfileManager(QuickProfileManager, urwid.WidgetWrap):
|
||||||
self.pass_wid = sat_widgets.Password(_("Password:"), align="center")
|
self.pass_wid = sat_widgets.Password(_("Password:"), align="center")
|
||||||
|
|
||||||
style = ["no_first_select"]
|
style = ["no_first_select"]
|
||||||
profiles = host.bridge.profiles_list_get()
|
|
||||||
profiles.sort()
|
|
||||||
self.list_profile = sat_widgets.List(
|
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
|
# new & delete buttons
|
||||||
buttons = [
|
buttons = [
|
||||||
|
|
|
||||||
|
|
@ -54,25 +54,32 @@ class Progress(urwid.WidgetWrap, quick_widgets.QuickWidget):
|
||||||
|
|
||||||
def progress_cb(self, loop, data):
|
def progress_cb(self, loop, data):
|
||||||
progress_id, message, profile = data
|
progress_id, message, profile = data
|
||||||
data = self.host.bridge.progress_get(progress_id, profile)
|
|
||||||
pbar = self.progress_dict[(progress_id, profile)]["progress"]
|
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 data:
|
||||||
if self.progress_dict[(progress_id, profile)]["state"] == "init":
|
if entry["state"] == "init":
|
||||||
# first answer, we must construct the bar
|
# first answer, we must construct the bar
|
||||||
self.progress_dict[(progress_id, profile)]["state"] = "progress"
|
entry["state"] = "progress"
|
||||||
pbar.done = float(data["size"])
|
pbar.done = float(data["size"])
|
||||||
|
|
||||||
pbar.set_completion(float(data["position"]))
|
pbar.set_completion(float(data["position"]))
|
||||||
self.update_not_bar()
|
self.update_not_bar()
|
||||||
else:
|
else:
|
||||||
if self.progress_dict[(progress_id, profile)]["state"] == "progress":
|
if entry["state"] == "progress":
|
||||||
self.progress_dict[(progress_id, profile)]["state"] = "done"
|
entry["state"] = "done"
|
||||||
pbar.set_completion(pbar.done)
|
pbar.set_completion(pbar.done)
|
||||||
self.update_not_bar()
|
self.update_not_bar()
|
||||||
return
|
return
|
||||||
|
|
||||||
loop.set_alarm_in(0.2, self.progress_cb, (progress_id, message, profile))
|
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):
|
def _remove_bar(self, progress_id, profile):
|
||||||
wid = self.progress_dict[(progress_id, profile)]["full"]
|
wid = self.progress_dict[(progress_id, profile)]["full"]
|
||||||
self.progress_list.remove(wid)
|
self.progress_list.remove(wid)
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,12 @@ class StatusBar(urwid.Columns):
|
||||||
urwid.connect_signal(self.status, "click", self.on_status_click)
|
urwid.connect_signal(self.status, "click", self.on_status_click)
|
||||||
|
|
||||||
def on_presence_click(self, sender=None):
|
def on_presence_click(self, sender=None):
|
||||||
if not self.host.bridge.is_connected(
|
def show_presence_popup(connected):
|
||||||
self.host.current_profile
|
if not connected:
|
||||||
): # FIXME: manage multi-profiles
|
|
||||||
return
|
return
|
||||||
options = [commonConst.PRESENCE[presence] for presence in commonConst.PRESENCE]
|
options = [
|
||||||
|
commonConst.PRESENCE[presence] for presence in commonConst.PRESENCE
|
||||||
|
]
|
||||||
list_widget = sat_widgets.GenericList(
|
list_widget = sat_widgets.GenericList(
|
||||||
options=options,
|
options=options,
|
||||||
option_type=sat_widgets.ClickableText,
|
option_type=sat_widgets.ClickableText,
|
||||||
|
|
@ -60,10 +61,14 @@ class StatusBar(urwid.Columns):
|
||||||
)
|
)
|
||||||
self.host.show_pop_up(decorated)
|
self.host.show_pop_up(decorated)
|
||||||
|
|
||||||
|
# FIXME: manage multi-profiles
|
||||||
|
self.host.bridge.is_connected(
|
||||||
|
self.host.current_profile, callback=show_presence_popup
|
||||||
|
)
|
||||||
|
|
||||||
def on_status_click(self, sender=None):
|
def on_status_click(self, sender=None):
|
||||||
if not self.host.bridge.is_connected(
|
def show_status_popup(connected):
|
||||||
self.host.current_profile
|
if not connected:
|
||||||
): # FIXME: manage multi-profiles
|
|
||||||
return
|
return
|
||||||
pop_up_widget = sat_widgets.InputDialog(
|
pop_up_widget = sat_widgets.InputDialog(
|
||||||
_("Set your status"),
|
_("Set your status"),
|
||||||
|
|
@ -74,6 +79,11 @@ class StatusBar(urwid.Columns):
|
||||||
)
|
)
|
||||||
self.host.show_pop_up(pop_up_widget)
|
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
|
||||||
|
)
|
||||||
|
|
||||||
def on_change(self, sender=None, user_data=None):
|
def on_change(self, sender=None, user_data=None):
|
||||||
new_value = user_data.get_text()
|
new_value = user_data.get_text()
|
||||||
previous = (
|
previous = (
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ dependencies = [
|
||||||
"twisted[tls] >= 22.10",
|
"twisted[tls] >= 22.10",
|
||||||
"twomemo >= 1.0.0, < 2",
|
"twomemo >= 1.0.0, < 2",
|
||||||
"txdbus",
|
"txdbus",
|
||||||
"urwid >= 1.2.0, < 3",
|
"urwid >= 4.2.5, < 5",
|
||||||
"urwid-satext",
|
"urwid-satext",
|
||||||
"xmlschema < 4.3.1",
|
"xmlschema < 4.3.1",
|
||||||
"tx-xmpp>=0.15",
|
"tx-xmpp>=0.15",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue