Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion source/NVDAState.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2022-2023 NV Access Limited
# Copyright (C) 2022-2025 NV Access Limited
# This file may be used under the terms of the GNU General Public License, version 2 or later.
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html

Expand Down Expand Up @@ -83,6 +83,10 @@ def speechDictDefaultFile(self) -> str:
def updateCheckStateFile(self) -> str:
return os.path.join(self.configDir, "updateCheckState.pickle")

@property
def guiStateFile(self) -> str:
return os.path.join(self.configDir, "guiState.ini")

def getSymbolsConfigFile(self, locale: str) -> str:
return os.path.join(self.configDir, f"symbols-{locale}.dic")

Expand Down
54 changes: 50 additions & 4 deletions source/_remoteClient/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
from typing import TypedDict
from urllib import request

import wx.lib
import wx.lib.agw

import gui
import wx
from wx.lib.expando import ExpandoTextCtrl
from wx.lib.agw import persist
from gui.contextHelp import ContextHelpMixin
from logHandler import log
from gui.guiHelper import alwaysCallAfter, BoxSizerHelper
Expand Down Expand Up @@ -204,6 +208,7 @@ def __init__(self, parent: wx.Window | None = None, id: int = wx.ID_ANY):
min=1,
max=65535,
initial=SERVER_PORT,
name="remote.connect.port",
)
# Translators: Label of the edit field to enter key (password) to secure the Remote Access connection.
self.key = sizerHelper.addLabeledControl(pgettext("remote", "&Key"), wx.TextCtrl)
Expand Down Expand Up @@ -313,32 +318,44 @@ def __init__(self, parent: wx.Window, id: int, title: str, hostnames: list[str]
super().__init__(parent, id, title=title)
mainSizer = wx.BoxSizer(wx.VERTICAL)
contentsSizerHelper = BoxSizerHelper(self, wx.VERTICAL)
self._persistentControls: list[wx.Window] = []
self._connectionModeControl = contentsSizerHelper.addLabeledControl(
# Translators: Label of the control allowing users to set whether they are the controlling or controlled computer in the Remote Access connection dialog.
pgettext("remote", "&Mode:"),
wx.Choice,
choices=tuple(mode.displayString for mode in RemoteConnectionMode),
# For persistence
name="remote.connect.mode",
)
# if not persist.PersistenceManager.Get().RegisterAndRestore(self._connectionModeControl):
# self._connectionModeControl.SetSelection(0)
Comment thread
SaschaCowley marked this conversation as resolved.
Resolved
Comment thread
SaschaCowley marked this conversation as resolved.
Resolved
self._connectionModeControl.SetSelection(0)
self._persistentControls.append(self._connectionModeControl)
self._clientOrServerControl = contentsSizerHelper.addLabeledControl(
# Translators: Label of the control allowing users to select whether to use a pre-existing Remote Access server, or to run their own.
pgettext("remote", "&Server:"),
wx.Choice,
choices=tuple(serverType.displayString for serverType in RemoteServerType.__members__.values()),
# For persistence
name="remote.connect.server",
)
self._clientOrServerControl.Bind(wx.EVT_CHOICE, self._onClientOrServer)
self._clientOrServerControl.SetSelection(0)
self._persistentControls.append(self._clientOrServerControl)
simpleBook = self._simpleBook = wx.Simplebook(self)
self._clientPanel = ClientPanel(simpleBook)
if hostnames:
self._clientPanel.host.AppendItems(hostnames)
self._clientPanel.host.SetSelection(0)
self._serverPanel = ServerPanel(simpleBook)
self._persistentControls.append(self._serverPanel.port)
# Since wx.SimpleBook doesn't create a page switcher for us, the following page labels are not used in the GUI.
simpleBook.AddPage(self._clientPanel, "Client")
simpleBook.AddPage(self._serverPanel, "Server")
self._clientOrServerControl.SetSelection(0)
self._selectedPanel = self._clientPanel
contentsSizerHelper.addItem(simpleBook)
# Initialise persistence
self._registerAndRestorePersistentControls()
self._doSyncChoiceAndBook()
contentsSizerHelper.addDialogDismissButtons(wx.OK | wx.CANCEL, True)
self.Bind(wx.EVT_BUTTON, self._onOk, id=wx.ID_OK)
mainSizer.Add(contentsSizerHelper.sizer, border=guiHelper.BORDER_FOR_DIALOGS, flag=wx.ALL)
Expand All @@ -347,15 +364,39 @@ def __init__(self, parent: wx.Window, id: int, title: str, hostnames: list[str]
self.CenterOnScreen()
self._connectionModeControl.SetFocus()
self.Bind(wx.EVT_SHOW, self._onShow)
# EVT_CLOSE is not fired on modal dialogs
# Use EVT_WINDOW_DESTROY instead
self.Bind(wx.EVT_WINDOW_DESTROY, self._onDestroy)

def _registerAndRestorePersistentControls(self):
persistenceManager = persist.PersistenceManager.Get()
for control in self._persistentControls:
persistenceManager.RegisterAndRestore(control)

def _savePersistentControls(self):
persistenceManager = persist.PersistenceManager.Get()
for control in self._persistentControls:
persistenceManager.Save(control)

def _unregisterPersistentControls(self):
persistenceManager = persist.PersistenceManager.Get()
for control in self._persistentControls:
persistenceManager.Unregister(control)

def _onClientOrServer(self, evt: wx.CommandEvent) -> None:
"""Respond to changing between using a control server or hosting it locally"""
self._doSyncChoiceAndBook()
evt.Skip()

def _doSyncChoiceAndBook(self):
"""Set the page of the wx.SimpleBook to correspond to the selection in the server type control."""
selectedIndex = self._clientOrServerControl.GetSelection()
self._simpleBook.ChangeSelection(selectedIndex)
# Hack: setting or changing the selection of a wx.SimpleBook seems to cause focus to jump to the first focusable control in the newly selected page, so force focus back to the control that caused the change.
# Hack: setting or changing the selection of a wx.SimpleBook
# seems to cause focus to jump to the first focusable control in the newly selected page,
# so force focus back to the control that caused the change.
self._clientOrServerControl.SetFocus()
self._selectedPanel = self._simpleBook.GetPage(selectedIndex)
evt.Skip()

def _onOk(self, evt: wx.CommandEvent) -> None:
"""Respond to the OK button being pressed."""
Expand Down Expand Up @@ -387,8 +428,13 @@ def _onOk(self, evt: wx.CommandEvent) -> None:
if focusTarget is not None:
focusTarget.SetFocus()
else:
self._savePersistentControls()
evt.Skip()

def _onDestroy(self, evt: wx.WindowDestroyEvent):
self._unregisterPersistentControls()
evt.Skip()

def _getKey(self) -> str:
"""Get the connection key."""
return self._selectedPanel.key.GetValue()
Expand Down
2 changes: 2 additions & 0 deletions source/config/configSpec.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@
__many__ = string(default="")
[[ui]]
confirmDisconnectAsFollower = boolean(default=True)
# 0: follower, 1: leader
Comment thread
SaschaCowley marked this conversation as resolved.
Resolved
lastConnectionMode = integer(default=0, min=0, max=1)
Comment thread
SaschaCowley marked this conversation as resolved.
Resolved
Comment thread
SaschaCowley marked this conversation as resolved.
Resolved
"""

#: The configuration specification
Expand Down
8 changes: 8 additions & 0 deletions source/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import warnings
import wx
import wx.adv
import wx.lib.agw.persist

import globalVars
import tones
Expand Down Expand Up @@ -886,9 +887,16 @@ def initialize():

monkeyPatches.applyWxMonkeyPatches(mainFrame, winUser, wx)

# Set up GUI persistence
persistenceManager = wx.lib.agw.persist.PersistenceManager.Get()
persistenceManager.SetPersistenceFile(NVDAState.WritePaths.guiStateFile)
if not NVDAState.shouldWriteToDisk():
persistenceManager.DisableSaving()


def terminate():
global mainFrame
wx.lib.agw.persist.PersistenceManager.Free()
mainFrame = None


Expand Down
2 changes: 2 additions & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* NVDA no longer resets braille tables to automatic when changing its language. (#18538, @LeonarddeR)
* The Dot Pad braille display driver now supports automatic detection of USB-connected devices.
Note that this is disabled by default due to the device using generic USB identifiers, but can be enabled in braille settings. (#18444, @bramd)
* The NVDA Remote Access connection dialog now remembers the most recent connection mode, server type and locally hosted port of manual connections. (#18512)

### Bug Fixes

Expand All @@ -47,6 +48,7 @@ Please refer to [the developer guide](https://download.nvaccess.org/documentatio
* detours to `9764cebcb1a75940e68fa83d6730ffaf0f669401`. (#18447, @LeonarddeR)
* The `nvda_dmp` utility has been removed. (#18480, @codeofdusk)
* `comInterfaces_sconscript` has been updated to make the generated files in `comInterfaces` work better with IDEs. (#17608, @gexgd0419)
* NVDA now configures `wx.lib.agw.persist.PersistenceManager` on GUI initialisation. (#18601)

#### Deprecations

Expand Down