Repository navigation
Honor screenEdgeGapTopNotch when it is set to 0 - #1911
Conversation
screenEdgeGapTopNotch was read with UserDefaults.float(forKey:), which returns 0 for a missing key, so adjustedVisibleFrame treated 0 as "not set" and applied screenEdgeGapTop on notched screens instead. Store it as an optional double so an unset key still falls back to screenEdgeGapTop, while an explicit value, including 0, is used on screens with a notch. An unset value is exported as an empty entry and clears the setting on import. Configs exported by earlier versions always contain a float 0, so a legacy float 0 is still imported as not set. Fixes #1658
|
Thanks for working on this problem! I think this could also be solved by using a DoubleDefault with a default value of, say, |
|
Good call, that's simpler. Switched to |
Drops OptionalDoubleDefault. A negative screenEdgeGapTopNotch means it isn't set and the regular top gap applies; 0 now removes the gap on notched screens. Legacy exports that stored 0 still import as not set. Co-authored-by: Ryan Hanson <ryan@ryanhanson.dev>
e3d3007 to
dfbeca4
Compare
Fixes #1658.
Problem
screenEdgeGapTopNotchwas aFloatDefault, which readsUserDefaults.float(forKey:). That returns 0 when the key is missing, so a missing key and an explicit 0 looked the same.adjustedVisibleFrameusedvalue != 0to mean "set", soscreenEdgeGapTopNotch = 0fell back toscreenEdgeGapTopon screens with a notch. WithscreenEdgeGapTop = 30, the only way to get no top gap on the built-in display was a tiny non-zero value such as-0.00001.Fix
OptionalDoubleDefault, modeled onDoubleDefault. Its value isnilwhen the key is missing, and setting it back tonilremoves the key.screenEdgeGapTopNotch. On a screen with a notch, any set value, including 0, replacesscreenEdgeGapTop. When it is not set,screenEdgeGapTopis used as before. Non-zero values and screens without a notch behave as before.doubleand an unset value as an empty entry ({}). Importing the empty entry clears the setting, so "unset" and 0 stay distinct through export and import."screenEdgeGapTopNotch": {"float": 0}even when it was never set, so a legacy float 0 is still imported as "not set", following howDoubleDefaulthandles legacy float values (Allow zero to disable minimum window size limits #1802). Other legacy values are imported as they are.defaults delete.One behavior change to be aware of: importing one of those older configs used to store 0 for this key. Someone who imported such a config before this change, has
screenEdgeGapTopset and uses a notched display will get no top gap on that display after updating.defaults delete com.knollsoft.Rectangle screenEdgeGapTopNotchrestores the fallback.Tests
Added
ScreenEdgeGapTests. It callsadjustedVisibleFrame()onNSScreentest doubles with and without a top safe-area inset, withscreenEdgeGapTop = 30:mainthis fails withXCTAssertEqual failed: ("30.0") is not equal to ("0.0").{"float": 0}entry imports as not set (30 on the notched screen), and a legacy non-zero value is usedI ran the full RectangleTests bundle against a Debug build (Xcode 27.0, macOS 27.0): 572 tests, and the new tests pass. Some existing tests fail in my setup on both
mainand this branch, so they are unrelated to this change:ShortcutRecordingObserverTests.testRecordingAMouseButtonEndsRecordingand two tests inWindowActionShortcutViewTestsfail on every run, and three tests inSnappingManagerSessionTestsfail in some runs (they read the real front window's full-screen state).