diff --git a/Sources/ConfigureSheetController.swift b/Sources/ConfigureSheetController.swift index 4790dad..c4b91d0 100644 --- a/Sources/ConfigureSheetController.swift +++ b/Sources/ConfigureSheetController.swift @@ -21,7 +21,7 @@ final class ConfigureSheetController: NSObject { let window: NSWindow - private var settings = NecoSaverSettings.load() + private var settings = NecoSaverSettings.standard private let onDismiss: (_ saved: Bool) -> Void private var rows: [Row] = [] private let pictureCheckbox = NSButton(checkboxWithTitle: "Picture:", target: nil, action: nil) @@ -38,10 +38,29 @@ final class ConfigureSheetController: NSObject { ) super.init() window.title = "NecoSaver" + // ARC owns this window through `window`; leaving the default on would let a + // host that closes the sheet release it a second time. + window.isReleasedWhenClosed = false buildInterface() + reload() + } + + /// Re-reads the stored settings into the controls. The same sheet is handed to + /// the host every time it asks, so this is what makes the second opening show + /// what is stored rather than where the first one was left. + func reload() { + settings = Self.stored() refresh() } + /// The saved settings, with the picture seeded the same way the saver seeds it, + /// so the field shows the path the cats are actually walking on. + private static func stored() -> NecoSaverSettings { + var settings = NecoSaverSettings.load() + settings.seedPicturePath(with: Wallpaper.desktopPicturePath(for: nil) ?? "") + return settings + } + // MARK: - Interface private func buildInterface() { diff --git a/Sources/NecoSaverView.swift b/Sources/NecoSaverView.swift index e7bd530..c514c14 100644 --- a/Sources/NecoSaverView.swift +++ b/Sources/NecoSaverView.swift @@ -8,8 +8,8 @@ import ScreenSaver @objc(NecoSaverView) final class NecoSaverView: ScreenSaverView { private let engine: NecoSaverEngine - /// Held strongly: the host releases its reference once the sheet is up, and a - /// deallocated controller takes the sheet's targets with it. + /// Built once and kept: the host releases its reference once the sheet is up, + /// and a deallocated controller takes the sheet's window and targets with it. private var sheetController: ConfigureSheetController? override init?(frame: NSRect, isPreview: Bool) { @@ -62,19 +62,26 @@ final class NecoSaverView: ScreenSaverView { true } + /// Hosts read this more than once, and can replace the view between reading it + /// and presenting it. Building a controller per read would hand back a window + /// whose only owner was dropped by the very next read — and the sheet then + /// simply never appears. One controller, kept, re-reading the settings each + /// time it is handed over. override var configureSheet: NSWindow? { - let controller = ConfigureSheetController { [weak self] saved in - guard let self else { return } - if saved { - engine.apply(settings()) - loadWallpaper() - } - sheetController = nil - } + let controller = sheetController ?? makeSheetController() sheetController = controller + controller.reload() return controller.window } + private func makeSheetController() -> ConfigureSheetController { + ConfigureSheetController { [weak self] saved in + guard let self, saved else { return } + engine.apply(settings()) + loadWallpaper() + } + } + /// Reads a picture only when one is going to be drawn — decoding one costs /// several megabytes that a grey backdrop has no use for. private func loadWallpaper() {