Skip to content

Commit ec79d87

Browse files
committed
Added multiple screen support and improved transition between connecting/disconnecting screens.
1 parent e374577 commit ec79d87

File tree

8 files changed

+63
-33
lines changed

8 files changed

+63
-33
lines changed

Yippy.xcodeproj/project.pbxproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,7 +1341,7 @@
13411341
"@executable_path/../Frameworks",
13421342
);
13431343
MACOSX_DEPLOYMENT_TARGET = 10.13;
1344-
MARKETING_VERSION = 2.3.0;
1344+
MARKETING_VERSION = 2.4.0;
13451345
PRODUCT_BUNDLE_IDENTIFIER = MatthewDavidson.Yippy;
13461346
PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
13471347
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1367,7 +1367,7 @@
13671367
"@executable_path/../Frameworks",
13681368
);
13691369
MACOSX_DEPLOYMENT_TARGET = 10.13;
1370-
MARKETING_VERSION = 2.3.0;
1370+
MARKETING_VERSION = 2.4.0;
13711371
PRODUCT_BUNDLE_IDENTIFIER = MatthewDavidson.Yippy;
13721372
PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
13731373
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1541,7 +1541,7 @@
15411541
"@executable_path/../Frameworks",
15421542
);
15431543
MACOSX_DEPLOYMENT_TARGET = 10.13;
1544-
MARKETING_VERSION = 2.3.0;
1544+
MARKETING_VERSION = 2.4.0;
15451545
PRODUCT_BUNDLE_IDENTIFIER = MatthewDavidson.YippyBeta;
15461546
PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
15471547
PRODUCT_NAME = "$(TARGET_NAME) Beta";
@@ -1666,7 +1666,7 @@
16661666
"@executable_path/../Frameworks",
16671667
);
16681668
MACOSX_DEPLOYMENT_TARGET = 10.13;
1669-
MARKETING_VERSION = 2.3.0;
1669+
MARKETING_VERSION = 2.4.0;
16701670
PRODUCT_BUNDLE_IDENTIFIER = MatthewDavidson.YippyBeta;
16711671
PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
16721672
PRODUCT_NAME = "$(TARGET_NAME) Beta";
@@ -1798,7 +1798,7 @@
17981798
"@executable_path/../Frameworks",
17991799
);
18001800
MACOSX_DEPLOYMENT_TARGET = 10.13;
1801-
MARKETING_VERSION = 2.3.0;
1801+
MARKETING_VERSION = 2.4.0;
18021802
PRODUCT_BUNDLE_IDENTIFIER = MatthewDavidson.YippyXCTest;
18031803
PRODUCT_MODULE_NAME = "$(PRODUCT_NAME:c99extidentifier)";
18041804
PRODUCT_NAME = "$(TARGET_NAME)";

Yippy/Sources/Extensions/NSMenuItem+Functional.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Cocoa
1010

1111
extension NSMenuItem {
1212

13+
@discardableResult
1314
func with(submenu: NSMenu) -> NSMenuItem {
1415
self.submenu = submenu
1516
return self

Yippy/Sources/Models/Controller.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class Controller {
5656
self.statusItem.menu = Self.createMenu(settings: settings, state: state, target: self)
5757

5858
// Create yippy window controller
59-
self.yippyWindowController = Self.createYippyWindowController(isHistoryPanelShown: state.isHistoryPanelShown, panelPosition: state.panelPosition, disposeBag: state.disposeBag)
59+
self.yippyWindowController = Self.createYippyWindowController(state: state, disposeBag: state.disposeBag)
6060

6161
// Create preview window controllers
6262
self.previewWindowController = Self.createPreviewWindowController(previewItem: state.previewHistoryItem, disposeBag: state.disposeBag)
@@ -152,13 +152,13 @@ class Controller {
152152
}
153153
}
154154

155-
static func createYippyWindowController(isHistoryPanelShown: BehaviorRelay<Bool>, panelPosition: BehaviorRelay<PanelPosition>, disposeBag: DisposeBag) -> YippyWindowController {
155+
static func createYippyWindowController(state: State, disposeBag: DisposeBag) -> YippyWindowController {
156156
let controller = YippyWindowController.createYippyWindowController()
157157
controller
158-
.subscribeTo(toggle: isHistoryPanelShown)
158+
.subscribeTo(toggle: state.isHistoryPanelShown)
159159
.disposed(by: disposeBag)
160160
controller
161-
.subscribePositionTo(position: panelPosition)
161+
.subscribeFrameTo(position: state.panelPosition.asObservable(), screen: state.currentScreen.asObservable())
162162
.disposed(by: disposeBag)
163163
return controller
164164
}

Yippy/Sources/Models/History/HistoryItem.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,16 @@ class HistoryItem: NSObject {
212212
pasteboard.setData(data, forType: .color)
213213
return NSColor(from: pasteboard)
214214
}
215+
216+
private func isStringLink(string: String) -> Bool {
217+
let types: NSTextCheckingResult.CheckingType = [.link]
218+
let detector = try? NSDataDetector(types: types.rawValue)
219+
guard (detector != nil && string.count > 0) else { return false }
220+
if detector!.numberOfMatches(in: string, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, string.count)) > 0 {
221+
return true
222+
}
223+
return false
224+
}
215225
}
216226

217227
// MARK: - HistoryItem+NSPasteboardWriting

Yippy/Sources/Models/PanelPosition.swift

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,32 @@ enum PanelPosition: Int, Codable, CaseIterable {
1919
case centerLarge = 6
2020
case fullScreen = 7
2121

22-
var frame: NSRect {
23-
// TODO: Use NSEvent.mouseLocation to choose which screen
22+
public func getFrame(forScreen screen: NSScreen) -> NSRect {
2423
switch self {
2524
case .right:
26-
return NSRect(x: NSScreen.main!.frame.maxX - Constants.panel.menuWidth, y: 0, width: Constants.panel.menuWidth, height: NSScreen.main!.frame.maxY)
25+
return NSRect(x: screen.frame.maxX - Constants.panel.menuWidth, y: screen.frame.minY, width: Constants.panel.menuWidth, height: screen.frame.maxY)
2726
case .left:
28-
return NSRect(x: 0, y: 0, width: Constants.panel.menuWidth, height: NSScreen.main!.frame.maxY)
27+
return NSRect(x: screen.frame.minX, y: screen.frame.minY, width: Constants.panel.menuWidth, height: screen.frame.maxY)
2928
case .top:
30-
return NSRect(x: 0, y: NSScreen.main!.frame.maxY - Constants.panel.menuHeight, width: NSScreen.main!.frame.width, height: Constants.panel.menuHeight)
29+
return NSRect(x: screen.frame.minX, y: screen.frame.maxY - Constants.panel.menuHeight, width: screen.frame.width, height: Constants.panel.menuHeight)
3130
case .bottom:
32-
return NSRect(x: 0, y: 0, width: NSScreen.main!.frame.width, height: Constants.panel.menuHeight)
31+
return NSRect(x: screen.frame.minX, y: screen.frame.minY, width: screen.frame.width, height: Constants.panel.menuHeight)
3332
case .centerSmall:
34-
let size = NSSize(width: NSScreen.main!.frame.width / 2, height: NSScreen.main!.frame.height / 2)
35-
return Self.centerRect(ofSize: size, inRect: NSScreen.main!.frame)
33+
let size = NSSize(width: screen.frame.width / 2, height: screen.frame.height / 2)
34+
return Self.centerRect(ofSize: size, inRect: screen.frame)
3635
case .centerMedium:
37-
let size = NSSize(width: NSScreen.main!.frame.width * 0.7, height: NSScreen.main!.frame.height * 0.7)
38-
return Self.centerRect(ofSize: size, inRect: NSScreen.main!.frame)
36+
let size = NSSize(width: screen.frame.width * 0.7, height: screen.frame.height * 0.7)
37+
return Self.centerRect(ofSize: size, inRect: screen.frame)
3938
case .centerLarge:
40-
let size = NSSize(width: NSScreen.main!.frame.width * 0.85, height: NSScreen.main!.frame.height * 0.85)
41-
return Self.centerRect(ofSize: size, inRect: NSScreen.main!.frame)
39+
let size = NSSize(width: screen.frame.width * 0.85, height: screen.frame.height * 0.85)
40+
return Self.centerRect(ofSize: size, inRect: screen.frame)
4241
case .fullScreen:
43-
return NSScreen.main!.frame
42+
return screen.frame
4443
}
4544
}
4645

4746
private static func centerRect(ofSize size: NSSize, inRect rect: NSRect) -> NSRect {
48-
return NSRect(origin: NSPoint(x: (rect.width - size.width) / 2, y: (rect.height - size.height) / 2), size: size)
47+
return NSRect(origin: NSPoint(x: (rect.width - size.width) / 2 + rect.minX, y: (rect.height - size.height) / 2 + rect.minY), size: size)
4948
}
5049

5150
var title: String {

Yippy/Sources/Models/State.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class State {
2424

2525
var panelPosition: BehaviorRelay<PanelPosition>
2626

27+
var currentScreen: BehaviorRelay<NSScreen>
28+
2729
var previewHistoryItem: BehaviorRelay<HistoryItem?>
2830

2931
var launchAtLogin: BehaviorRelay<Bool>
@@ -51,6 +53,7 @@ class State {
5153
self.launchAtLogin = BehaviorRelay<Bool>(value: LoginServiceKit.isExistLoginItems())
5254
self.showsRichText = BehaviorRelay<Bool>(value: settings.showsRichText)
5355
self.pastesRichText = BehaviorRelay<Bool>(value: settings.pastesRichText)
56+
self.currentScreen = BehaviorRelay<NSScreen>(value: Self.getCurrentScreen(forMouseLocation: NSEvent.mouseLocation))
5457
self.disposeBag = disposeBag
5558

5659
// Setup history
@@ -65,8 +68,8 @@ class State {
6568
// Setup pasteboard monitor
6669
self.pasteboardMonitor = PasteboardMonitor(pasteboard: NSPasteboard.general, changeCount: settings.pasteboardChangeCount, delegate: self.history)
6770

68-
//
6971
Self.monitorPastesRichText(state: self)
72+
Self.monitorMousePosition(state: self)
7073
}
7174

7275
// MARK: - Constructor Helpers
@@ -84,4 +87,22 @@ class State {
8487
HistoryItem.pastesRichText = $0
8588
}).disposed(by: state.disposeBag)
8689
}
90+
91+
static func monitorMousePosition(state: State) {
92+
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (_) in
93+
let currentScreen = getCurrentScreen(forMouseLocation: NSEvent.mouseLocation)
94+
if currentScreen != state.currentScreen.value {
95+
state.currentScreen.accept(currentScreen)
96+
}
97+
}
98+
}
99+
100+
static func getCurrentScreen(forMouseLocation location: NSPoint) -> NSScreen {
101+
for screen in NSScreen.screens {
102+
if screen.frame.contains(location) {
103+
return screen
104+
}
105+
}
106+
return NSScreen.main!
107+
}
87108
}

Yippy/Sources/Windows/Yippy/YippyWindowController.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,10 @@ class YippyWindowController: NSWindowController {
4444
})
4545
}
4646

47-
func subscribePositionTo(position: BehaviorRelay<PanelPosition>) -> Disposable {
48-
return position
49-
.subscribe(onNext: {
50-
[] in
51-
self.window?.setFrame($0.frame, display: true)
52-
})
47+
func subscribeFrameTo(position: Observable<PanelPosition>, screen: Observable<NSScreen>) -> Disposable {
48+
Observable.combineLatest(position, screen).subscribe(onNext: {
49+
(position, screen) in
50+
self.window?.setFrame(position.getFrame(forScreen: screen), display: true)
51+
})
5352
}
5453
}

YippyUITests/YippyUITests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,15 @@ class YippyUITests: XCTestCase {
106106
XCTAssertTrue(app.yippyWindow.exists)
107107

108108
// Check window location is .right
109-
XCTAssertEqual(app.yippyWindow.frame.midX, PanelPosition.right.frame.midX)
109+
XCTAssertEqual(app.yippyWindow.frame.midX, PanelPosition.right.getFrame(forScreen: NSScreen.main!).midX)
110110

111111
// Change to position left
112112
app.statusItemButton.click()
113113
app.positionButton.click()
114114
app.positionLeftButton.click()
115115

116116
// Check window location is .left
117-
XCTAssertEqual(app.yippyWindow.frame.midX, PanelPosition.left.frame.midX)
117+
XCTAssertEqual(app.yippyWindow.frame.midX, PanelPosition.left.getFrame(forScreen: NSScreen.main!).midX)
118118

119119
// Change to position bottom
120120
app.statusItemButton.click()
@@ -139,7 +139,7 @@ class YippyUITests: XCTestCase {
139139
app.positionRightButton.click()
140140

141141
// Check window location is .right
142-
XCTAssertEqual(app.yippyWindow.frame.midX, PanelPosition.right.frame.midX)
142+
XCTAssertEqual(app.yippyWindow.frame.midX, PanelPosition.right.getFrame(forScreen: NSScreen.main!).midX)
143143
}
144144

145145
func testEmptyYippyHistory() {

0 commit comments

Comments
 (0)