Enable pinch-zoom in find panel fields

This commit is contained in:
1024jp 2018-10-09 21:37:35 +09:00
parent f2acabeace
commit 69ab0762ee
5 changed files with 134 additions and 52 deletions

View File

@ -5,6 +5,11 @@ Change Log
3.6.2 (unreleased)
--------------------------
### New Features
- Let the input fields in the find panel accept pinch-zoom.
### Improvements
- Avoid selecting deleted spaces when undoing a soft tab deletion.

View File

@ -2,7 +2,6 @@
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="2Yd-sK-nho">
<dependencies>
<deployment identifier="macosx"/>
<development version="10000" identifier="xcode"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14313.18"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -66,7 +65,7 @@
<rect key="frame" x="0.0" y="0.0" width="460" height="146"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView focusRingType="exterior" verticalHuggingPriority="249" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" minMagnification="1" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8gM-hc-HhV">
<scrollView focusRingType="exterior" verticalHuggingPriority="249" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" allowsMagnification="YES" minMagnification="1" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8gM-hc-HhV" customClass="SynchronizedScrollView" customModule="CotEditor" customModuleProvider="target">
<rect key="frame" x="20" y="82" width="420" height="44"/>
<clipView key="contentView" id="vCs-g9-nGi" customClass="FindPanelTextClipView" customModule="CotEditor" customModuleProvider="target">
<rect key="frame" x="1" y="1" width="418" height="42"/>
@ -111,7 +110,7 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<scrollView focusRingType="exterior" verticalHuggingPriority="249" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" minMagnification="1" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AIf-o5-KHg">
<scrollView focusRingType="exterior" verticalHuggingPriority="249" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" allowsMagnification="YES" minMagnification="1" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AIf-o5-KHg" customClass="SynchronizedScrollView" customModule="CotEditor" customModuleProvider="target">
<rect key="frame" x="20" y="28" width="420" height="44"/>
<clipView key="contentView" id="vJP-0z-fPc" customClass="FindPanelTextClipView" customModule="CotEditor" customModuleProvider="target">
<rect key="frame" x="1" y="1" width="418" height="42"/>

View File

@ -102,6 +102,8 @@
2A18A5BF1C4A746A00BAD817 /* Encodings in Resources */ = {isa = PBXBuildFile; fileRef = 2A18A5BE1C4A746A00BAD817 /* Encodings */; };
2A1A8AFA207081D9004DFEBB /* InvisibleFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1A8AF9207081D9004DFEBB /* InvisibleFormatter.swift */; };
2A1A8AFB207081D9004DFEBB /* InvisibleFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1A8AF9207081D9004DFEBB /* InvisibleFormatter.swift */; };
2A1B7E75216CBBEA002C7395 /* SynchronizedScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1B7E74216CBBEA002C7395 /* SynchronizedScrollView.swift */; };
2A1B7E76216CBBEA002C7395 /* SynchronizedScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1B7E74216CBBEA002C7395 /* SynchronizedScrollView.swift */; };
2A1CD5541DD1AADE006E17DB /* NSAlert+ModalSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1CD5531DD1AADE006E17DB /* NSAlert+ModalSheet.swift */; };
2A1CD5551DD1AADE006E17DB /* NSAlert+ModalSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1CD5531DD1AADE006E17DB /* NSAlert+ModalSheet.swift */; };
2A1EB5C419AD469500C1E37E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2A1EB5C319AD469500C1E37E /* Assets.xcassets */; };
@ -851,6 +853,7 @@
2A18A5BC1C4A730D00BAD817 /* EncodingDetectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncodingDetectionTests.swift; sourceTree = "<group>"; };
2A18A5BE1C4A746A00BAD817 /* Encodings */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Encodings; path = TestFiles/Encodings; sourceTree = "<group>"; };
2A1A8AF9207081D9004DFEBB /* InvisibleFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvisibleFormatter.swift; sourceTree = "<group>"; };
2A1B7E74216CBBEA002C7395 /* SynchronizedScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizedScrollView.swift; sourceTree = "<group>"; };
2A1CD5531DD1AADE006E17DB /* NSAlert+ModalSheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAlert+ModalSheet.swift"; sourceTree = "<group>"; };
2A1EB5C319AD469500C1E37E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
2A1FAD5720A74D0A00566D7C /* MutableCopying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutableCopying.swift; sourceTree = "<group>"; };
@ -1409,6 +1412,7 @@
2A5D132E1D1FACC900D38E6A /* FindPanelLayoutManager.swift */,
2A4AF76620759BE500C47606 /* RegexFindPanelTextView.swift */,
2A0778602072040500876277 /* RegularExpressionSyntaxType.swift */,
2A1B7E74216CBBEA002C7395 /* SynchronizedScrollView.swift */,
);
name = "Text View";
sourceTree = "<group>";
@ -2837,6 +2841,7 @@
2A78BFA01D1AF82A00A583D2 /* IntegrationPaneController.swift in Sources */,
2AA375441D403F100080C27C /* String+Encoding.swift in Sources */,
2A1FAD5920A74D0A00566D7C /* MutableCopying.swift in Sources */,
2A1B7E76216CBBEA002C7395 /* SynchronizedScrollView.swift in Sources */,
2A6FD9DE1D392EFF00A59784 /* ATSTypesetter.swift in Sources */,
2A5D13491D1FEF9900D38E6A /* FindPanelFieldViewController.swift in Sources */,
2ACC5E421E7B08D300109ABC /* MultipleReplacementViewController.swift in Sources */,
@ -3073,6 +3078,7 @@
2A8C338F1D3E1C040005B0B7 /* IncompatibleCharacter.swift in Sources */,
2A9BF3C41D382BB100E3D3E2 /* EditorTextView+Transformation.swift in Sources */,
2A3A922B205638A600F9B5F3 /* BackgroundView.swift in Sources */,
2A1B7E75216CBBEA002C7395 /* SynchronizedScrollView.swift in Sources */,
2A42579E1D229A8B0086DAAD /* OutlineParseOperation.swift in Sources */,
2AE73F3D2039A29300D8903B /* URL+ExtendedAttribute.swift in Sources */,
2A0DD6331E655C4A001CAAA3 /* TokenTextView.swift in Sources */,

View File

@ -25,7 +25,7 @@
import Cocoa
final class FindPanelFieldViewController: NSViewController, NSTextViewDelegate, NSUserInterfaceValidations {
final class FindPanelFieldViewController: NSViewController, NSTextViewDelegate {
// MARK: Private Properties
@ -128,54 +128,6 @@ final class FindPanelFieldViewController: NSViewController, NSTextViewDelegate,
// MARK: Action Messages
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
guard let action = item.action else { return false }
switch action {
case #selector(smallerFont):
guard let scrollView = self.findTextView?.enclosingScrollView else { return false }
return scrollView.magnification > scrollView.minMagnification
case #selector(biggerFont):
guard let scrollView = self.findTextView?.enclosingScrollView else { return false }
return scrollView.magnification < scrollView.maxMagnification
default:
break
}
return true
}
/// scale up
@IBAction func biggerFont(_ sender: Any?) {
for textView in [self.findTextView, self.replacementTextView] {
textView?.enclosingScrollView?.animator().magnification += 0.2
}
}
/// scale down
@IBAction func smallerFont(_ sender: Any?) {
for textView in [self.findTextView, self.replacementTextView] {
textView?.enclosingScrollView?.animator().magnification -= 0.2
}
}
/// reset scale and font to default
@IBAction func resetFont(_ sender: Any?) {
for textView in [self.findTextView, self.replacementTextView] {
textView?.enclosingScrollView?.animator().magnification = 1.0
}
}
/// show regular expression reference as popover
@IBAction func showRegexHelp(_ sender: Any?) {

View File

@ -0,0 +1,120 @@
//
// SynchronizedScrollView.swift
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2018-10-09.
//
// ---------------------------------------------------------------------------
//
// © 2018 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Cocoa
final class SynchronizedScrollView: NSScrollView {
// MARK: Scroll View Methods
/// receive pinch zoom event
override func magnify(with event: NSEvent) {
let magnification = self.magnification + event.magnification
let location = self.contentView.convert(event.locationInWindow, from: nil)
for scrollView in self.siblings {
scrollView.setMagnification(magnification, centeredAt: location)
}
}
/// receive double-tap event adjusting scale
override func smartMagnify(with event: NSEvent) {
for scrollView in self.siblings {
scrollView.syncedSmartMagnify(with: event)
}
}
// MARK: Private Methods
/// auto-founded scroll views to sync (including the receiver itself)
private var siblings: [SynchronizedScrollView] {
return self.superview?.subviews.compactMap { $0 as? SynchronizedScrollView } ?? [self]
}
/// invoking super's `smartMagnify(with:)` without the issue about the cycle invoking
private func syncedSmartMagnify(with event: NSEvent) {
super.smartMagnify(with: event)
}
}
// MARK: Actions
extension SynchronizedScrollView: NSUserInterfaceValidations {
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
guard let action = item.action else { return false }
switch action {
case #selector(smallerFont):
return self.magnification > self.minMagnification
case #selector(biggerFont):
return self.magnification < self.maxMagnification
default:
return true
}
}
/// scale up
@IBAction func biggerFont(_ sender: Any?) {
for scrollView in self.siblings {
scrollView.animator().magnification += 0.2
}
}
/// scale down
@IBAction func smallerFont(_ sender: Any?) {
for scrollView in self.siblings {
scrollView.animator().magnification -= 0.2
}
}
/// reset scale to default
@IBAction func resetFont(_ sender: Any?) {
for scrollView in self.siblings {
scrollView.animator().magnification = 1.0
}
}
}