Implemented window size and position persistence

This commit is contained in:
Mark Qvist 2025-11-04 14:19:46 +01:00
parent 88e8db6118
commit 00b9517458
2 changed files with 149 additions and 17 deletions

View file

@ -29,13 +29,51 @@ import RNS.vendor.umsgpack as msgpack
WINDOW_DEFAULT_WIDTH = 494
WINDOW_DEFAULT_HEIGHT = 800
WINDOW_HEIGHT_MARGIN = 0
WINDOW_MIN_WIDTH = 415
WINDOW_MIN_HEIGHT = 550
app_ui_scaling_path = None
app_ui_wcfg_path = None
app_ui_window_config = None
app_ui_dsp_width = None
app_ui_dsp_height = None
app_init_window_state = "normal"
def get_display_res():
global app_ui_dsp_width, app_ui_dsp_height
if not RNS.vendor.platformutils.is_linux(): return None, None
else:
try:
import subprocess
cmd_xrandr = subprocess.Popen(['xrandr'], stdout=subprocess.PIPE)
cmd_grep = subprocess.Popen(['grep', '*'], stdin=cmd_xrandr.stdout, stdout=subprocess.PIPE)
cmd_xrandr.stdout.close(); res_bytes, _ = cmd_grep.communicate()
resolution = res_bytes.split()[0]
width, height = resolution.split(b'x')
app_ui_dsp_width = int(width)
app_ui_dsp_height = int(height)
return app_ui_dsp_width, app_ui_dsp_height
except Exception as e:
RNS.log(f"Could not get display resolution: {e}", RNS.LOG_WARNING)
RNS.trace_exception(e)
return None, None
app_ui_scaling_path = None
def apply_ui_scale():
global app_ui_scaling_path
global app_ui_wcfg_path
global app_ui_window_config
default_scale = os.environ["KIVY_METRICS_DENSITY"] if "KIVY_METRICS_DENSITY" in os.environ else "unknown"
config_path = None
config_path = None
ui_scale_path = None
ui_wcfg_path = None
res_ident = ""
dsp_width, dsp_height = get_display_res()
if dsp_width and dsp_height:
RNS.log(f"Got display res: {dsp_width}x{dsp_height}", RNS.LOG_DEBUG)
res_ident = f"_{dsp_width}_{dsp_height}"
try:
if RNS.vendor.platformutils.is_android():
@ -50,7 +88,12 @@ def apply_ui_scale():
else:
ui_scale_path = config_path+"/app_storage/ui_scale"
if ui_scale_path:
ui_scale_path = f"{ui_scale_path}{res_ident}"
ui_wcfg_path = f"{ui_scale_path}_windowcfg"
app_ui_scaling_path = ui_scale_path
app_ui_wcfg_path = ui_wcfg_path
except Exception as e:
RNS.log(f"Error while locating UI scale file: {e}", RNS.LOG_ERROR)
@ -58,11 +101,10 @@ def apply_ui_scale():
if ui_scale_path != None:
RNS.log("Default scaling factor on this platform is "+str(default_scale), RNS.LOG_NOTICE)
try:
RNS.log("Looking for scaling info in "+str(ui_scale_path))
RNS.log("Looking for scaling info in "+str(ui_scale_path), RNS.LOG_NOTICE)
if os.path.isfile(ui_scale_path):
scale_factor = None
with open(ui_scale_path, "r") as sf:
scale_factor = float(sf.readline())
with open(ui_scale_path, "r") as sf: scale_factor = float(sf.readline())
if scale_factor != None:
if scale_factor >= 0.3 and scale_factor <= 5.0:
@ -75,6 +117,22 @@ def apply_ui_scale():
except Exception as e:
RNS.log(f"Error while reading UI scaling factor: {e}", RNS.LOG_ERROR)
if ui_scale_path != None:
try:
RNS.log("Looking for saved window configuration in "+str(ui_wcfg_path), RNS.LOG_NOTICE)
if os.path.isfile(ui_wcfg_path):
scale_factor = None
with open(ui_wcfg_path, "r") as sf: window_config = sf.readline().split()
if window_config != None:
RNS.log(f"READ SAVED WINDOW CONFIGURATION: {window_config}")
if type(window_config) == list and len(window_config) == 5:
app_ui_window_config = window_config
except Exception as e:
RNS.log(f"Error while reading saved window configuration: {e}", RNS.LOG_ERROR)
if args.export_settings:
from .sideband.core import SidebandCore
sideband = SidebandCore(
@ -200,33 +258,85 @@ else:
apply_ui_scale()
if not RNS.vendor.platformutils.is_android():
scaling_factor = 1.0
# Set default scaling factor and position
scaling_factor = 1.0
window_target_x = None
window_target_y = None
window_state = "normal"
# Attempt to read configured scaling factor
# from environment variable
if not RNS.vendor.platformutils.is_windows() and not RNS.vendor.platformutils.is_darwin():
try: scaling_factor = float(os.environ["KIVY_METRICS_DENSITY"])
except Exception as e: pass
# Bound scaling factor to reasonable values
if scaling_factor < 0.75: scaling_factor = 0.75
if scaling_factor > 2: scaling_factor = 2
model = None
max_width = WINDOW_DEFAULT_WIDTH*scaling_factor
max_height = WINDOW_DEFAULT_HEIGHT*scaling_factor
# Get reasonable maximum window bounds
if app_ui_dsp_width and app_ui_dsp_height:
# Use display resolution if available
max_width = app_ui_dsp_width
max_height = app_ui_dsp_height-WINDOW_HEIGHT_MARGIN
else:
# Assume bounds from default size * scaling
max_width = WINDOW_DEFAULT_WIDTH*scaling_factor
max_height = WINDOW_DEFAULT_HEIGHT*scaling_factor
# Try to find device model to apply reasonable
# bounds on window sizes
model = None
try:
if os.path.isfile("/sys/firmware/devicetree/base/model"):
with open("/sys/firmware/devicetree/base/model", "r") as mf:
model = mf.read()
except: pass
# Apply window sizing based on model
if model:
if model.startswith("Raspberry Pi "): max_height = 625
# Decrease default height for Raspberry Pi
# if screen resolution is unavailable, to
# avoid overflow on small screens
if model.startswith("Raspberry Pi ") and not app_ui_dsp_height: max_height = 625
window_width = int(min(WINDOW_DEFAULT_WIDTH*scaling_factor, max_width))
window_height = int(min(WINDOW_DEFAULT_HEIGHT*scaling_factor, max_height))
# Initialize size to defaults
window_width_target = int(WINDOW_DEFAULT_WIDTH)
window_height_target = int(WINDOW_DEFAULT_HEIGHT)
# But use saved window configuration if possible
if type(app_ui_window_config) == list and len(app_ui_window_config) == 5:
RNS.log(f"USING SAVED WINDOW CONFIGURATION: {app_ui_window_config}", RNS.LOG_NOTICE)
window_width_target = int(app_ui_window_config[0])
window_height_target = int(app_ui_window_config[1])
window_target_x = int(app_ui_window_config[2])
window_target_y = int(app_ui_window_config[3])
window_state = app_ui_window_config[4]
if not window_state in ["maximized", "minimized", "normal"]: window_state = "normal"
if window_target_x < 0: window_target_x = 0
if window_target_y < 0: window_target_y = 0
# Calculate final window size
window_width = int(max(min(window_width_target, max_width), WINDOW_MIN_WIDTH))
window_height = int(max(min(window_height_target, max_height), WINDOW_MIN_HEIGHT))
if app_ui_dsp_width and app_ui_dsp_height and window_target_x and window_target_y:
if window_target_x > (app_ui_dsp_width - window_width): window_target_x = app_ui_dsp_width - window_width
if window_target_y > (app_ui_dsp_height - window_height): window_target_y = app_ui_dsp_height - window_height
from kivy.config import Config
Config.set("graphics", "width", str(window_width))
Config.set("graphics", "height", str(window_height))
if window_target_x and window_target_y:
Config.set('graphics', 'position', 'custom')
Config.set("graphics", "left", str(window_target_x))
Config.set("graphics", "top", str(window_target_y))
if window_state == "maximized":
Config.set("graphics", "window_state", "maximized")
app_init_window_state = window_state
from kivymd.app import MDApp
app_superclass = MDApp
@ -410,6 +520,7 @@ class SidebandApp(MDApp):
self.key_ptt_down = False
Window.softinput_mode = "below_target"
self.window_state = app_init_window_state
self.icon = self.sideband.asset_dir+"/icon.png"
self.notification_icon = self.sideband.asset_dir+"/notification_icon.png"
@ -709,6 +820,18 @@ class SidebandApp(MDApp):
self.apply_eink_mods()
self.set_bars_colors()
def save_window_config(self):
try:
if not RNS.vendor.platformutils.is_android():
wcfg = f"{Window.width} {Window.height} {Window.left} {Window.top} {self.window_state}"
if app_ui_wcfg_path == None: RNS.log("No path to UI window config file could be found, cannot save window config", RNS.LOG_ERROR)
else:
try:
with open(app_ui_wcfg_path, "w") as sfile: sfile.write(str(wcfg))
RNS.log(f"Saved window config to {app_ui_wcfg_path}", RNS.LOG_DEBUG)
except Exception as e: RNS.log(f"Error while saving window config to {app_ui_wcfg_path}: {e}", RNS.LOG_ERROR)
except Exception as e: RNS.log("Error while saving window configuration: {e}", RNS.LOG_ERROR)
def update_ui_theme(self):
if self.sideband.config["dark_ui"]:
self.theme_cls.theme_style = "Dark"
@ -877,11 +1000,15 @@ class SidebandApp(MDApp):
self.app_state = SidebandApp.STOPPING
RNS.log("App stopped", RNS.LOG_DEBUG)
def on_maximize(self, sender): self.window_state = "maximized"
def on_minimize(self, sender): self.window_state = "minimized"
def on_restore(self, sender): self.window_state = "normal"
def is_in_foreground(self):
if self.app_state == SidebandApp.ACTIVE:
return True
else:
return False
if self.app_state == SidebandApp.ACTIVE: return True
else: return False
def perform_paused_check(self, delta_time):
# This workaround mitigates yet another bug in Kivy
@ -1400,6 +1527,10 @@ class SidebandApp(MDApp):
Window.bind(on_request_close=self.close_requested)
Window.bind(on_drop_file=self.file_dropped)
Window.bind(on_maximize=self.on_maximize)
Window.bind(on_minimize=self.on_minimize)
Window.bind(on_restore=self.on_restore)
if __variant__ != "":
variant_str = " "+__variant__
else:

View file

@ -1032,6 +1032,7 @@ class SidebandCore():
def save_configuration(self):
self.__save_config()
if self.is_standalone: self.owner_app.save_window_config()
def set_active_propagation_node(self, dest):
if dest == None: