Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
55 changes: 38 additions & 17 deletions Rectangle/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -2863,11 +2863,10 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="9VW-Hc-lh2" customClass="AutoSaveFloatField" customModule="Rectangle" customModuleProvider="target">
<rect key="frame" x="102" y="0.0" width="100" height="21"/>
<textField focusRingType="none" horizontalHuggingPriority="200" verticalHuggingPriority="750" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="9VW-Hc-lh2" customClass="AutoSaveFloatField" customModule="Rectangle" customModuleProvider="target">
<rect key="frame" x="102" y="0.0" width="87" height="21"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="100" id="A6q-mA-Xaq"/>
<constraint firstAttribute="width" priority="250" constant="100" id="Ebz-nb-cys"/>
<constraint firstAttribute="width" constant="87" id="1tz-tU-hh9"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" alignment="right" title="400" drawsBackground="YES" id="lUu-Hs-vuk">
<numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" formatWidth="-1" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" maximumFractionDigits="3" id="O5b-ir-jyO"/>
Expand All @@ -2876,27 +2875,38 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="249" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="eTN-Lz-EDf">
<rect key="frame" x="208" y="3" width="105" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="px" id="HVG-6v-eJH">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" translatesAutoresizingMaskIntoConstraints="NO" id="Pon-Fc-IkL">
<rect key="frame" x="319" y="1" width="131" height="20"/>
<popUpButton verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7Kx-vz-WQp">
<rect key="frame" x="194" y="-4" width="121" height="26"/>
<popUpButtonCell key="cell" type="push" title="px" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="5LN-YU-Bwy" id="NYb-xH-9ZP">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="message"/>
<menu key="menu" id="hR2-Wt-Cnz">
<items>
<menuItem title="px" state="on" tag="1" id="5LN-YU-Bwy"/>
<menuItem title="%" tag="2" id="AgV-lP-6XB"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="setTodoWidthUnit:" target="yhc-gS-h02" id="TlX-cp-y1c"/>
</connections>
</popUpButton>
<customView hidden="YES" translatesAutoresizingMaskIntoConstraints="NO" id="62c-ee-hzw" userLabel="Horizontal Spacer">
<rect key="frame" x="265" y="0.0" width="50" height="50"/>
</customView>
<stackView distribution="fill" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Pon-Fc-IkL">
<rect key="frame" x="319" y="0.0" width="131" height="21"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZQD-06-YVI">
<rect key="frame" x="-2" y="2" width="63" height="16"/>
<rect key="frame" x="-2" y="3" width="63" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Todo side" id="O9a-3Q-HB1">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rB8-CF-Yrt">
<rect key="frame" x="64" y="-4" width="71" height="25"/>
<popUpButton verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rB8-CF-Yrt">
<rect key="frame" x="64" y="-3" width="71" height="25"/>
<popUpButtonCell key="cell" type="push" title="Right" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="DG2-BN-GPE" id="wsh-YF-QuF">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="message"/>
Expand All @@ -2922,17 +2932,26 @@
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstItem="9VW-Hc-lh2" firstAttribute="top" secondItem="Pon-Fc-IkL" secondAttribute="top" id="33P-4K-AiO"/>
<constraint firstItem="9VW-Hc-lh2" firstAttribute="baseline" secondItem="90f-Y4-sgi" secondAttribute="baseline" id="OIb-DR-UkV"/>
<constraint firstItem="9VW-Hc-lh2" firstAttribute="centerY" secondItem="7Kx-vz-WQp" secondAttribute="centerY" id="UhI-UY-xEm"/>
<constraint firstItem="9VW-Hc-lh2" firstAttribute="bottom" secondItem="62c-ee-hzw" secondAttribute="bottom" id="sxH-AK-sOj"/>
<constraint firstItem="9VW-Hc-lh2" firstAttribute="top" secondItem="7Kx-vz-WQp" secondAttribute="top" id="tis-la-5GJ"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<stackView identifier="Toggle todo" distribution="fill" orientation="horizontal" alignment="centerY" spacing="10" horizontalStackHuggingPriority="1000" verticalStackHuggingPriority="249.99998474121094" horizontalHuggingPriority="1000" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nsh-GW-hpI">
Expand Down Expand Up @@ -3160,6 +3179,7 @@
<constraint firstAttribute="width" priority="999" constant="450" id="Db3-zB-dtQ"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="450" id="OsX-gR-mRY"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="150" id="UiW-yY-DW4"/>
<constraint firstAttribute="trailing" secondItem="Pon-Fc-IkL" secondAttribute="trailing" id="ZzM-1H-aHN"/>
<constraint firstAttribute="height" priority="750" constant="150" id="bDD-of-gYL"/>
</constraints>
<visibilityPriorities>
Expand Down Expand Up @@ -3230,6 +3250,7 @@
<outlet property="subsequentExecutionPopUpButton" destination="pVH-s3-FHn" id="tdS-iJ-EXc"/>
<outlet property="todoAppSidePopUpButton" destination="rB8-CF-Yrt" id="oNn-QO-3IC"/>
<outlet property="todoAppWidthField" destination="9VW-Hc-lh2" id="o0g-BR-XOK"/>
<outlet property="todoAppWidthUnitPopUpButton" destination="7Kx-vz-WQp" id="Hfq-La-GEy"/>
<outlet property="todoCheckbox" destination="0PP-0x-QWc" id="F1A-VF-cT5"/>
<outlet property="todoView" destination="lRr-k7-YZR" id="Hcn-7a-WBS"/>
<outlet property="todoViewHeightConstraint" destination="89R-oK-9JB" id="xTV-rY-WiU"/>
Expand Down
1 change: 1 addition & 0 deletions Rectangle/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class Defaults {
static let todoMode = BoolDefault(key: "todoMode")
static let todoApplication = StringDefault(key: "todoApplication")
static let todoSidebarWidth = FloatDefault(key: "todoSidebarWidth", defaultValue: 400)
static let todoSidebarWidthUnit = IntEnumDefault<TodoSidebarWidthUnit>(key: "todoSidebarWidthUnit", defaultValue: .pixels)
static let todoSidebarSide = IntEnumDefault<TodoSidebarSide>(key: "todoSidebarSide", defaultValue: .right)
static let snapModifiers = IntDefault(key: "snapModifiers")
static let attemptMatchOnNextPrevDisplay = OptionalBoolDefault(key: "attemptMatchOnNextPrevDisplay")
Expand Down
14 changes: 14 additions & 0 deletions Rectangle/PrefsWindow/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SettingsViewController: NSViewController {
@IBOutlet weak var todoCheckbox: NSButton!
@IBOutlet weak var todoView: NSStackView!
@IBOutlet weak var todoAppWidthField: AutoSaveFloatField!
@IBOutlet weak var todoAppWidthUnitPopUpButton: NSPopUpButton!
@IBOutlet weak var todoAppSidePopUpButton: NSPopUpButton!
@IBOutlet weak var toggleTodoShortcutView: MASShortcutView!
@IBOutlet weak var reflowTodoShortcutView: MASShortcutView!
Expand Down Expand Up @@ -145,6 +146,18 @@ class SettingsViewController: NSViewController {
aboutTodoWindowController?.showWindow(self)
}

@IBAction func setTodoWidthUnit(_ sender: NSPopUpButton) {
let tag = sender.selectedTag()
guard let unit = TodoSidebarWidthUnit(rawValue: tag) else {
Logger.log("Expected a pop up button to have a selected item with a valid tag matching a value of TodoSidebarWidthUnit. Got: \(String(describing: tag))")
return
}

Defaults.todoSidebarWidthUnit.value = unit

TodoManager.moveAllIfNeeded(false)
}

@IBAction func setTodoAppSide(_ sender: NSPopUpButton) {
let tag = sender.selectedTag()
guard let side = TodoSidebarSide(rawValue: tag) else {
Expand Down Expand Up @@ -267,6 +280,7 @@ class SettingsViewController: NSViewController {
todoAppWidthField.defaultsSetAction = {
TodoManager.moveAllIfNeeded(false)
}
todoAppWidthUnitPopUpButton.selectItem(withTag: Defaults.todoSidebarWidthUnit.value.rawValue)
todoAppSidePopUpButton.selectItem(withTag: Defaults.todoSidebarSide.value.rawValue)
TodoManager.initToggleShortcut()
TodoManager.initReflowShortcut()
Expand Down
18 changes: 15 additions & 3 deletions Rectangle/ScreenDetection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,22 @@ extension NSScreen {
}

if !ignoreTodo, Defaults.todo.userEnabled, Defaults.todoMode.enabled, TodoManager.todoScreen == self, TodoManager.hasTodoWindow() {
if Defaults.todoSidebarSide.value == .left {
newFrame.origin.x += Defaults.todoSidebarWidth.cgFloat
switch Defaults.todoSidebarWidthUnit.value {
case .pixels:
if Defaults.todoSidebarSide.value == .left {
newFrame.origin.x += Defaults.todoSidebarWidth.cgFloat
}
newFrame.size.width -= Defaults.todoSidebarWidth.cgFloat
case .pct:
if (Defaults.todoSidebarWidth.cgFloat > 100) {
return newFrame;
}

if Defaults.todoSidebarSide.value == .left {
newFrame.origin.x += newFrame.size.width * (Defaults.todoSidebarWidth.cgFloat * 0.01)
}
newFrame.size.width *= 1 - (Defaults.todoSidebarWidth.cgFloat * 0.01)
}
newFrame.size.width -= Defaults.todoSidebarWidth.cgFloat
}

if Defaults.screenEdgeGapsOnMainScreenOnly.enabled, self != NSScreen.screens.first {
Expand Down
42 changes: 35 additions & 7 deletions Rectangle/TodoMode/TodoManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,28 @@ class TodoManager {
adjustedVisibleFrame = screen.adjustedVisibleFrame(true)
var sharedEdge: Edge
var rect = adjustedVisibleFrame
switch Defaults.todoSidebarSide.value {
case .left:
sharedEdge = .right
case .right:
sharedEdge = .left
rect.origin.x = adjustedVisibleFrame.maxX - Defaults.todoSidebarWidth.cgFloat
let isRightSide = Defaults.todoSidebarSide.value == .right

sharedEdge = isRightSide ? .left : .right

switch Defaults.todoSidebarWidthUnit.value {
case .pixels:
if isRightSide {
rect.origin.x = adjustedVisibleFrame.maxX - Defaults.todoSidebarWidth.cgFloat
}
rect.size.width = Defaults.todoSidebarWidth.cgFloat
case .pct:
if (Defaults.todoSidebarWidth.cgFloat > 100) {
return;
}
Comment on lines +182 to +184

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 this is pretty weak error handling (here and in ScreenDetection#L166)

I didn't want to edit the UI too much to display any error text/add a negative interaction, but if this lack of feedback might be unclear for the user then I'm happy to change this!

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, this is great!

The existing pixel input doesn't include any feedback for values that don't make sense, so to me it's acceptable to add this in without guardrails. If you're inclined to add anything in, though, I see no reason to turn it down. The other edge case that's weird (pre-existing) is if you delete the value or make it really small then the window is unnecessarily small until the value is changed.

One thing that might make sense to do as part of this PR is to automatically handle conversion between pixels and percentage when the dropdown is changed. This should be simple, since you can just grab the main screen dimensions and use those.

Let me know if you want to add more to the PR, otherwise I'm good with merging it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the feedback! it's really appreciated

converting between pixels and percentage is a great idea -- added this in 646a400

this is ready to merge once you're good with the new changes :)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice! Good catch on using the Todo screen rather than the main screen.


let computedWidth = adjustedVisibleFrame.width * (Defaults.todoSidebarWidth.cgFloat * 0.01)
if isRightSide {
rect.origin.x = adjustedVisibleFrame.maxX - computedWidth
}
rect.size.width = computedWidth
}
rect.size.width = Defaults.todoSidebarWidth.cgFloat

rect = rect.screenFlipped

if Defaults.gapSize.value > 0 {
Expand Down Expand Up @@ -245,3 +259,17 @@ enum TodoSidebarSide: Int {
case right = 1
case left = 2
}

enum TodoSidebarWidthUnit: Int, CustomStringConvertible {
case pixels = 1
case pct = 2

var description: String {
switch self {
case .pixels:
return "px"
case .pct:
return "%"
}
}
}
9 changes: 8 additions & 1 deletion Rectangle/WindowCalculation/LeftTodoCalculation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ final class LeftTodoCalculation: WindowCalculation {
override func calculateRect(_ params: RectCalculationParameters) -> RectResult {
let visibleFrameOfScreen = params.visibleFrameOfScreen
var calculatedWindowRect = visibleFrameOfScreen
var calculatedTodoSidebarWidth: CGFloat

if Defaults.todoSidebarWidthUnit.value == .pixels {
calculatedTodoSidebarWidth = Defaults.todoSidebarWidth.cgFloat
} else {
calculatedTodoSidebarWidth = visibleFrameOfScreen.width * (Defaults.todoSidebarWidth.cgFloat * 0.01)
}

calculatedWindowRect.size.width = Defaults.todoSidebarWidth.cgFloat
calculatedWindowRect.size.width = calculatedTodoSidebarWidth

return RectResult(calculatedWindowRect, subAction: .leftTodo)
}
Expand Down
Loading