diff --git a/McBopomofo.xcodeproj/xcshareddata/xcschemes/McBopomofo.xcscheme b/McBopomofo.xcodeproj/xcshareddata/xcschemes/McBopomofo.xcscheme index 494e3900..05268ef2 100644 --- a/McBopomofo.xcodeproj/xcshareddata/xcschemes/McBopomofo.xcscheme +++ b/McBopomofo.xcodeproj/xcshareddata/xcschemes/McBopomofo.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> diff --git a/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift b/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift index 0e63fc3c..2d1cbfba 100644 --- a/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift +++ b/Packages/CandidateUI/Sources/CandidateUI/CandidateController.swift @@ -99,6 +99,16 @@ public class CandidateController: NSWindowController { UInt.max } + /// Sets the location of the candidate window. + /// + /// Please note that the method has side effects that modifies + /// `windowTopLeftPoint` to make the candidate window to stay in at least + /// in a screen. + /// + /// - Parameters: + /// - windowTopLeftPoint: The given location. + /// - height: The height that helps the window not to be out of the bottom + /// of a screen. @objc(setWindowTopLeftPoint:bottomOutOfScreenAdjustmentHeight:) public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: CGFloat) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()) { @@ -130,7 +140,7 @@ public class CandidateController: NSWindowController { // bottom beneath the screen? if adjustedPoint.y - windowSize.height < screenFrame.minY { - adjustedPoint.y = screenFrame.minY + adjustedHeight + windowSize.height + adjustedPoint.y = windowTopLeftPoint.y + adjustedHeight + windowSize.height } // top over the screen? diff --git a/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift b/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift index 62a2d816..efb43533 100644 --- a/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift +++ b/Packages/CandidateUI/Tests/CandidateUITests/HorizontalCandidateControllerTests.swift @@ -27,11 +27,10 @@ class HorizontalCandidateControllerTests: XCTestCase { controller.keyLabels = ["1", "2", "3", "4"] controller.reloadData() controller.visible = true - controller.set(windowTopLeftPoint: NSPoint(x: -100, y: -100), bottomOutOfScreenAdjustmentHeight: 10) + controller.set(windowTopLeftPoint: NSPoint(x: -100, y: 0), bottomOutOfScreenAdjustmentHeight: 10) let exp = expectation(description: "wait") _ = XCTWaiter.wait(for: [exp], timeout: 0.2) XCTAssert(controller.window?.frame.minX ?? -1 >= 0) - XCTAssert(controller.window?.frame.minY ?? -1 >= 0) } func testPositioning2() { diff --git a/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift b/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift index 601da6de..b5b00c6c 100644 --- a/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift +++ b/Packages/CandidateUI/Tests/CandidateUITests/VerticalCandidateControllerTests.swift @@ -27,11 +27,10 @@ class VerticalCandidateControllerTests: XCTestCase { controller.keyLabels = ["1", "2", "3", "4"] controller.reloadData() controller.visible = true - controller.set(windowTopLeftPoint: NSPoint(x: -100, y: -100), bottomOutOfScreenAdjustmentHeight: 10) + controller.set(windowTopLeftPoint: NSPoint(x: -100, y: 0), bottomOutOfScreenAdjustmentHeight: 10) let exp = expectation(description: "wait") _ = XCTWaiter.wait(for: [exp], timeout: 0.2) XCTAssert(controller.window?.frame.minX ?? -1 >= 0) - XCTAssert(controller.window?.frame.minY ?? -1 >= 0) } func testPositioning2() { diff --git a/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift index 76ea5c40..70031d7f 100644 --- a/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift +++ b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift @@ -46,12 +46,47 @@ public class TooltipController: NSWindowController { window?.orderOut(nil) } - private func set(windowLocation location: NSPoint) { - var newPoint = location - if location.y > 5 { - newPoint.y -= 5 + private func set(windowLocation windowTopLeftPoint: NSPoint) { + + var adjustedPoint = windowTopLeftPoint + adjustedPoint.y -= 5 + + var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero + for screen in NSScreen.screens { + let frame = screen.visibleFrame + if windowTopLeftPoint.x >= frame.minX && + windowTopLeftPoint.x <= frame.maxX && + windowTopLeftPoint.y >= frame.minY && + windowTopLeftPoint.y <= frame.maxY { + screenFrame = frame + break + } } - window?.setFrameTopLeftPoint(newPoint) + + let windowSize = window?.frame.size ?? NSSize.zero + + // bottom beneath the screen? + if adjustedPoint.y - windowSize.height < screenFrame.minY { + adjustedPoint.y = screenFrame.minY + windowSize.height + } + + // top over the screen? + if adjustedPoint.y >= screenFrame.maxY { + adjustedPoint.y = screenFrame.maxY - 1.0 + } + + // right + if adjustedPoint.x + windowSize.width >= screenFrame.maxX { + adjustedPoint.x = screenFrame.maxX - windowSize.width + } + + // left + if adjustedPoint.x < screenFrame.minX { + adjustedPoint.x = screenFrame.minX + } + + window?.setFrameTopLeftPoint(adjustedPoint) + } private func adjustSize() { diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 9ef30ee8..2c503790 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -1372,7 +1372,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [client attributesForCharacterIndex:cursor lineHeightRectangle:&lineHeightRect]; } @catch (NSException *exception) { - NSLog(@"%@", exception); + NSLog(@"lineHeightRectangle %@", exception); } if (useVerticalMode) { @@ -1383,6 +1383,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } gCurrentCandidateController.visible = YES; + } #pragma mark - User phrases