Implement Quick Action bar

This commit is contained in:
1024jp 2023-11-22 21:31:36 +09:00
parent b219ce991b
commit 3019629591
18 changed files with 710 additions and 4 deletions

View File

@ -7,6 +7,7 @@ Change Log
### New Features
- Add Quick Action bar which can launch by the Command-K keys.
- Insert scanned text in a photo taken by iPhone or iPad.

View File

@ -285,6 +285,12 @@
2A5DCE861D1888D800D5D74C /* SyntaxMappingConflictsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5DCE851D1888D800D5D74C /* SyntaxMappingConflictsView.swift */; };
2A5DCE871D1888D800D5D74C /* SyntaxMappingConflictsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5DCE851D1888D800D5D74C /* SyntaxMappingConflictsView.swift */; };
2A5DCE8A1D18FFDB00D5D74C /* EncodingListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5DCE881D18FFDB00D5D74C /* EncodingListView.swift */; };
2A5E41052B0AEFBB00D5EA20 /* QuickActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5E41042B0AEFBB00D5EA20 /* QuickActionView.swift */; };
2A5E41062B0AEFBB00D5EA20 /* QuickActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5E41042B0AEFBB00D5EA20 /* QuickActionView.swift */; };
2A5E41082B0AF62100D5EA20 /* QuickActionWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5E41072B0AF62100D5EA20 /* QuickActionWindowController.swift */; };
2A5E41092B0AF62100D5EA20 /* QuickActionWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5E41072B0AF62100D5EA20 /* QuickActionWindowController.swift */; };
2A5E410B2B0B559300D5EA20 /* ActionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5E410A2B0B559300D5EA20 /* ActionCommand.swift */; };
2A5E410C2B0B559400D5EA20 /* ActionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5E410A2B0B559300D5EA20 /* ActionCommand.swift */; };
2A5E6FC12A72342700E33EA7 /* UnicodeNormalization.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2A5E6FC02A72342700E33EA7 /* UnicodeNormalization.xcstrings */; };
2A5E6FC22A72342700E33EA7 /* UnicodeNormalization.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2A5E6FC02A72342700E33EA7 /* UnicodeNormalization.xcstrings */; };
2A5E6FC42A723CEA00E33EA7 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2A5E6FC32A723CE900E33EA7 /* InfoPlist.xcstrings */; };
@ -1037,6 +1043,9 @@
2A5DCE851D1888D800D5D74C /* SyntaxMappingConflictsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyntaxMappingConflictsView.swift; sourceTree = "<group>"; };
2A5DCE881D18FFDB00D5D74C /* EncodingListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncodingListView.swift; sourceTree = "<group>"; };
2A5DD44122793B9B0057AAD1 /* fr */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = fr; path = fr.lproj/ReportTemplate.md; sourceTree = "<group>"; };
2A5E41042B0AEFBB00D5EA20 /* QuickActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickActionView.swift; sourceTree = "<group>"; };
2A5E41072B0AF62100D5EA20 /* QuickActionWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickActionWindowController.swift; sourceTree = "<group>"; };
2A5E410A2B0B559300D5EA20 /* ActionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionCommand.swift; sourceTree = "<group>"; };
2A5E6FC02A72342700E33EA7 /* UnicodeNormalization.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = UnicodeNormalization.xcstrings; path = mul.lproj/UnicodeNormalization.xcstrings; sourceTree = "<group>"; };
2A5E6FC32A723CE900E33EA7 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = InfoPlist.xcstrings; path = mul.lproj/InfoPlist.xcstrings; sourceTree = "<group>"; };
2A5E6FC62A723F3C00E33EA7 /* ServicesMenu.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = ServicesMenu.xcstrings; path = mul.lproj/ServicesMenu.xcstrings; sourceTree = "<group>"; };
@ -2043,6 +2052,16 @@
name = Extensions;
sourceTree = "<group>";
};
2A5E410D2B0CE4DB00D5EA20 /* Quick Action Bar */ = {
isa = PBXGroup;
children = (
2A5E41072B0AF62100D5EA20 /* QuickActionWindowController.swift */,
2A5E41042B0AEFBB00D5EA20 /* QuickActionView.swift */,
2A5E410A2B0B559300D5EA20 /* ActionCommand.swift */,
);
name = "Quick Action Bar";
sourceTree = "<group>";
};
2A6602EB1D05E04E003E8D87 /* Accessory Views */ = {
isa = PBXGroup;
children = (
@ -2374,6 +2393,7 @@
children = (
2A5D13091D1ED10400D38E6A /* ConsolePanelController.swift */,
2A4257AF1D22FD490086DAAD /* ColorCodePanelController.swift */,
2A5E410D2B0CE4DB00D5EA20 /* Quick Action Bar */,
);
name = Panels;
sourceTree = "<group>";
@ -2787,9 +2807,9 @@
buildActionMask = 2147483647;
files = (
2A39F15726F74C2500B52876 /* AccessibleStepper.swift in Sources */,
2A5E410B2B0B559300D5EA20 /* ActionCommand.swift in Sources */,
2AF29EC42882EE7700DF31D2 /* AdvancedCharacterCounter.swift in Sources */,
2A04E9C327FEFA86008C82D8 /* AdvancedCharacterCounterView.swift in Sources */,
2A73B9332A8F6620002F3A16 /* RegexTextField.swift in Sources */,
2ACDC09B1D172CDE009B72D6 /* AntialiasingTextField.swift in Sources */,
2AC2462F1D1BC70C00E46CFA /* AppDelegate.swift in Sources */,
2A2792931D1DACC400F3FC5D /* AppearancePaneController.swift in Sources */,
@ -2999,8 +3019,11 @@
2AA175FA2AC5634500F6462C /* PopoverHolderView.swift in Sources */,
2A1856131D48AFEA008FA79E /* PrintPanelAccessoryController.swift in Sources */,
2AFAFD4B1D41487600F1458F /* PrintTextView.swift in Sources */,
2A5E41052B0AEFBB00D5EA20 /* QuickActionView.swift in Sources */,
2A5E41082B0AF62100D5EA20 /* QuickActionWindowController.swift in Sources */,
2A57B98F294ED75900771696 /* RangedIntegerFormatStyle.swift in Sources */,
2A4AF76820759BE500C47606 /* RegexFindPanelTextView.swift in Sources */,
2A73B9332A8F6620002F3A16 /* RegexTextField.swift in Sources */,
2A1814B921CF8BD500602214 /* RegularExpressionFormatter.swift in Sources */,
2A53F56727585A0E00ED16DF /* RegularExpressionReferenceView.swift in Sources */,
2A0778622072040500876277 /* RegularExpressionSyntaxType.swift in Sources */,
@ -3148,6 +3171,7 @@
buildActionMask = 2147483647;
files = (
2A39F15826F74C2500B52876 /* AccessibleStepper.swift in Sources */,
2A5E410C2B0B559400D5EA20 /* ActionCommand.swift in Sources */,
2AF29EC52882EE7700DF31D2 /* AdvancedCharacterCounter.swift in Sources */,
2A04E9C427FEFA87008C82D8 /* AdvancedCharacterCounterView.swift in Sources */,
2ACDC09A1D172CDE009B72D6 /* AntialiasingTextField.swift in Sources */,
@ -3257,7 +3281,6 @@
2AEAA8232096380C001A175C /* HighlightExtractors.swift in Sources */,
2A8458942A073C5F0056B1EA /* HighlightDefinition.swift in Sources */,
2AAD61F81D2BA3F5008FE772 /* HighlightParser.swift in Sources */,
2A73B9342A8F6620002F3A16 /* RegexTextField.swift in Sources */,
2AE95A1B2A86270000E85CF5 /* HoleContentView.swift in Sources */,
2A5D13131D1EE8FF00D38E6A /* HUDView.swift in Sources */,
2AE144B72B00A963005E8CF1 /* Identifiable.swift in Sources */,
@ -3361,7 +3384,10 @@
2A1856121D48AFEA008FA79E /* PrintPanelAccessoryController.swift in Sources */,
2AFAFD4A1D41487600F1458F /* PrintTextView.swift in Sources */,
2A57B990294ED75900771696 /* RangedIntegerFormatStyle.swift in Sources */,
2A5E41062B0AEFBB00D5EA20 /* QuickActionView.swift in Sources */,
2A5E41092B0AF62100D5EA20 /* QuickActionWindowController.swift in Sources */,
2A4AF76720759BE500C47606 /* RegexFindPanelTextView.swift in Sources */,
2A73B9342A8F6620002F3A16 /* RegexTextField.swift in Sources */,
2A1814B821CF8BD500602214 /* RegularExpressionFormatter.swift in Sources */,
2A53F56827585A0E00ED16DF /* RegularExpressionReferenceView.swift in Sources */,
2A0778612072040500876277 /* RegularExpressionSyntaxType.swift in Sources */,

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="22154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="22155" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22154"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22155"/>
</dependencies>
<scenes>
<!--Application-->
@ -609,6 +609,12 @@ Gw
<action selector="toggleStatusBar:" target="Ady-hI-5gd" id="pjH-8E-CJp"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WNT-nz-Qhz"/>
<menuItem title="Show Quick Actions…" keyEquivalent="k" id="7Su-OV-TgR">
<connections>
<action selector="showQuickActions:" target="Ady-hI-5gd" id="6AA-OS-m64"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="366"/>
<menuItem title="Split Editor" id="793">
<string key="keyEquivalent" base64-UTF8="YES">

View File

@ -0,0 +1,112 @@
//
// ActionCommand.swift
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2023-11-20.
//
// ---------------------------------------------------------------------------
//
// © 2023 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 AppKit
struct ActionCommand: Identifiable {
enum Kind {
case command
case outline
case script
}
let id = UUID()
var kind: Kind
var title: String
var paths: [String] = []
var shortcut: Shortcut?
var action: Selector
var tag: Int = 0
/// Perform the original menu action.
@discardableResult
func perform() -> Bool {
let sender = NSMenuItem()
sender.title = self.title
sender.action = self.action
sender.tag = self.tag
return NSApp.sendAction(self.action, to: nil, from: sender)
}
}
extension NSMenuItem {
/// The flat collection of `ActionCommand` representation including descendant items.
var actionCommands: [ActionCommand] {
if let submenu = self.submenu {
return submenu.items
.flatMap { $0.actionCommands }
.map {
var command = $0
command.paths.insert(self.title, at: 0)
return command
}
} else if let action = self.action, !self.isHidden, !ActionCommand.unsupportedActions.contains(action) {
self.validate()
return [ActionCommand(kind: (action == #selector(ScriptManager.launchScript)) ? .script : .command,
title: self.title, paths: [], shortcut: self.shortcut, action: action, tag: self.tag)]
} else {
return []
}
}
/// Validate the menu item so that the menu item properties, such as title, are updated to fit to the latest states.
private func validate() {
guard
let validator = self.target
?? self.action.flatMap({ NSApp.target(forAction: $0, to: self.target, from: self) }) as AnyObject?
else { return }
switch validator {
case let validator as any NSMenuItemValidation:
validator.validateMenuItem(self)
case let validator as any NSUserInterfaceValidations:
validator.validateUserInterfaceItem(self)
default:
break
}
}
}
private extension ActionCommand {
static let unsupportedActions: [Selector] = [
#selector(AppDelegate.showQuickActions),
]
}

View File

@ -332,6 +332,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
}
/// Show the Quick Action bar.
@IBAction func showQuickActions(_ sender: Any?) {
QuickActionWindowController.shared.showWindow(sender)
}
/// Show Snippet pane in the Settings window.
@IBAction func showSnippetEditor(_ sender: Any?) {

View File

@ -0,0 +1,301 @@
//
// QuickActionView.swift
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2023-11-20.
//
// ---------------------------------------------------------------------------
//
// © 2023 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 SwiftUI
import AppKit
struct QuickActionView: View {
struct Candidate: Identifiable {
var command: ActionCommand
var result: String.AbbreviatedMatchResult
var id: UUID { self.command.id }
}
@Environment(\.controlActiveState) private var controlActiveState
weak var parent: NSWindow?
@State var command: String = ""
@State var candidates: [Candidate] = []
@State private var commands: [ActionCommand] = []
@State private var selection: ActionCommand.ID?
@State private var keyMonitor: Any?
var body: some View {
VStack(spacing: 0) {
HStack(alignment: .firstTextBaseline) {
Image(systemName: "magnifyingglass")
TextField("Quick Actions", text: $command)
.onSubmit(self.perform)
.fontWeight(.light)
.textFieldStyle(.plain)
}
.font(.system(size: 22))
.padding(12)
if !self.candidates.isEmpty {
Divider()
ScrollViewReader { proxy in
ScrollView(.vertical) {
Group {
ForEach(self.candidates) { candidate in
ActionCommandView(command: candidate.command, matchedRanges: candidate.result.ranges)
.selected(candidate.id == self.selection)
.id(candidate.id)
.onTapGesture {
self.selection = candidate.id
self.perform()
}
}
}.padding(.horizontal, 12)
}.onChange(of: self.selection) { id in
proxy.scrollTo(id)
}
}
.padding(.vertical, 12)
.frame(maxHeight: 300)
.fixedSize(horizontal: false, vertical: true)
}
}
.onChange(of: self.command) { command in
self.candidates = self.commands
.compactMap {
guard let result = $0.title.abbreviatedMatch(with: command) else { return nil }
return Candidate(command: $0, result: result)
}
.sorted(\.result.score)
self.selection = self.candidates.first?.id
}
.onChange(of: self.controlActiveState) { state in
switch state {
case .key, .active:
self.commands = NSApp.mainMenu?.items
.flatMap(\.actionCommands) ?? []
self.keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
if let key = event.specialKey,
event.modifierFlags.isDisjoint(with: [.shift, .control, .option, .command])
{
switch key {
case .downArrow, .upArrow:
self.move(down: (key == .downArrow))
return nil
default:
break
}
}
return event
}
case .inactive:
self.command = ""
self.candidates.removeAll()
if let keyMonitor {
NSEvent.removeMonitor(keyMonitor)
self.keyMonitor = nil
}
@unknown default:
break
}
}
.frame(width: 500)
.ignoresSafeArea()
}
/// Move the selection to the next one, if any exists.
///
/// - Parameter down: Whether move down or up the selection.
private func move(down: Bool) {
guard
let index = self.candidates.firstIndex(where: { $0.id == self.selection }),
let candidate = self.candidates[safe: index + (down ? 1 : -1)]
else { return }
self.selection = candidate.id
}
/// Perform the selected command and close the view.
private func perform() {
if let command = self.candidates.first(where: { $0.id == self.selection })?.command {
command.perform()
}
self.parent?.close()
}
}
private struct ActionCommandView: View {
@Environment(\.colorSchemeContrast) var colorContrast
let command: ActionCommand
let matchedRanges: [Range<String.Index>]
var isSelected = false
var body: some View {
HStack {
Image(systemName: self.command.kind.systemImage)
.resizable()
.aspectRatio(contentMode: .fit)
.fontWeight(.light)
.foregroundStyle(self.isSelected ? .primary : .secondary)
.frame(width: 32, height: 20, alignment: .center)
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline, spacing: 4) {
Text(self.attributed(self.command.title, in: self.matchedRanges, font: .body))
}
.lineLimit(1)
.truncationMode(.tail)
.foregroundStyle((self.isSelected && self.colorContrast == .standard) ? Color.selectedMenuItemText.opacity(0.8) : .primary)
HStack(alignment: .firstTextBaseline, spacing: 4) {
ForEach(Array(self.command.paths.enumerated()), id: \.offset) { (offset, path) in
if offset > 0 {
Image(systemName: "chevron.compact.right")
.controlSize(.mini)
.opacity(0.5)
}
Text(path)
}
}
.foregroundStyle(self.isSelected ? .primary : .secondary)
.controlSize(.small)
}
Spacer()
if let shortcut = self.command.shortcut {
Text(shortcut.symbol)
.foregroundStyle(self.isSelected ? .primary : .secondary)
}
}
.padding(.vertical, 4)
.padding(.horizontal)
.contentShape(Rectangle()) // for click
.foregroundStyle(self.isSelected ? Color.selectedMenuItemText : .primary)
.background(self.isSelected ? Color.accentColor : .clear,
in: RoundedRectangle(cornerRadius: 6))
}
/// Set the selecting state of the receiver.
///
/// - Parameter selected: The selecting state to change.
func selected(_ selected: Bool = true) -> Self {
var view = self
view.isSelected = selected
return view
}
/// Return an AttributedString by highlighting the given ranges by taking the view states into account.
///
/// - Parameters:
/// - string: The base string to create the attributed string.
/// - ranges: The ranges for the given `string` to highlight.
/// - font: The base font.
/// - Returns: An attributed string.
private func attributed(_ string: String, in ranges: [Range<String.Index>], font: Font) -> AttributedString {
let attributed = AttributedString(string)
return ranges
.compactMap { Range($0, in: attributed) }
.reduce(into: attributed) { (string, range) in
string[range].font = font.weight(.bold)
string[range].foregroundColor = self.isSelected ? Color.selectedMenuItemText : nil
}
}
}
private extension ActionCommand.Kind {
var systemImage: String {
switch self {
case .command: "filemenu.and.selection"
case .outline: "list.bullet.rectangle"
case .script: "applescript.fill"
}
}
}
private extension Color {
static let selectedMenuItemText = Color(nsColor: .selectedMenuItemTextColor)
}
// MARK: - Preview
#Preview {
let candidates: [QuickActionView.Candidate] = [
.init(command: .init(kind: .command, title: "Find…",
paths: ["Find"],
shortcut: Shortcut("f", modifiers: .command),
action: #selector(NSResponder.yank)),
result: String.AbbreviatedMatchResult([], 0)),
.init(command: .init(kind: .command, title: "Fortran",
paths: ["Format", "Syntax"],
action: #selector(NSResponder.yank)),
result: String.AbbreviatedMatchResult([], 0)),
]
return QuickActionView(candidates: candidates)
}
#Preview("Command View") {
ActionCommandView(
command: .init(kind: .command, title: "Swift",
paths: ["Format", "Syntax"],
shortcut: Shortcut("s", modifiers: [.command]),
action: #selector(NSResponder.yank)),
matchedRanges: [],
isSelected: true
)
.fixedSize(horizontal: false, vertical: true)
.frame(width: 300)
.padding(12)
}

View File

@ -0,0 +1,131 @@
//
// QuickActionWindowController.swift
//
// CotEditor
// https://coteditor.com
//
// Created by 1024jp on 2023-11-20.
//
// ---------------------------------------------------------------------------
//
// © 2023 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 AppKit
import SwiftUI
final class QuickActionWindowController: NSWindowController {
static let shared = QuickActionWindowController()
init() {
let view = NSHostingViewSuppressingSafeArea(rootView: QuickActionView())
let panel = CommandBarPanel(contentRect: .zero, styleMask: [.titled, .fullSizeContentView], backing: .buffered, defer: false)
panel.contentView = view
panel.animationBehavior = .utilityWindow
panel.collectionBehavior.insert(.fullScreenAuxiliary)
panel.isFloatingPanel = true
panel.isMovableByWindowBackground = true
panel.titlebarAppearsTransparent = true
panel.titleVisibility = .hidden
panel.standardWindowButton(.closeButton)?.isHidden = true
panel.standardWindowButton(.zoomButton)?.isHidden = true
panel.standardWindowButton(.miniaturizeButton)?.isHidden = true
panel.center()
view.rootView.parent = panel
super.init(window: panel)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private final class CommandBarPanel: NSPanel {
override func cancelOperation(_ sender: Any?) {
super.cancelOperation(sender)
// -> Needs to close manually when a panel does not have a close button.
self.close()
}
override func resignKey() {
super.resignKey()
self.close()
}
}
/// Workaround for the issue that a window still keeps content height for the title bar even with `.ignoresSafeArea()`.
private final class NSHostingViewSuppressingSafeArea<T: View>: NSHostingView<T> {
private lazy var layoutGuide = NSLayoutGuide()
required init(rootView: T) {
super.init(rootView: rootView)
self.addLayoutGuide(self.layoutGuide)
NSLayoutConstraint.activate([
self.leadingAnchor.constraint(equalTo: self.layoutGuide.leadingAnchor),
self.topAnchor.constraint(equalTo: self.layoutGuide.topAnchor),
self.trailingAnchor.constraint(equalTo: self.layoutGuide.trailingAnchor),
self.bottomAnchor.constraint(equalTo: self.layoutGuide.bottomAnchor),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var safeAreaRect: NSRect {
self.frame
}
override var safeAreaInsets: NSEdgeInsets {
.zero
}
override var safeAreaLayoutGuide: NSLayoutGuide {
self.layoutGuide
}
override var additionalSafeAreaInsets: NSEdgeInsets {
get { .zero }
set { _ = newValue }
}
}

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "Schnellaktionen";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "Der absolute Pfad der abgelegten Datei.";

View File

@ -828,6 +828,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "Quick Actions";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "The dropped file absolute path.";

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "Acciones rápidas";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "La ruta absoluta del archivo eliminado.";

View File

@ -830,6 +830,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "Actions rapides";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "Le chemin absolu du fichier déposé.";

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "Azioni rapide";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "Percorso assoluto del file trascinato.";

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "クイックアクション";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "ドロップ元ファイルの絶対パス";

View File

@ -1297,6 +1297,78 @@
}
}
},
"7Su-OV-TgR.title" : {
"comment" : "Class = \"NSMenuItem\"; title = \"Show Quick Actions…\"; ObjectID = \"7Su-OV-TgR\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Schnellaktionen einblenden …"
}
},
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Show Quick Actions…"
}
},
"en-GB" : {
"stringUnit" : {
"state" : "translated",
"value" : "Show Quick Actions…"
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostrar las acciones rápidas…"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Afficher les actions rapides…"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostra azioni rapide…"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "クイックアクションを表示…"
}
},
"pt" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostrar Ações Rápidas…"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Hızlı Eylemleri Göster…"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "显示快速操作…"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "顯示快速動作…"
}
}
}
},
"8z7-Jc-agP.title" : {
"comment" : "Class = \"NSMenuItem\"; title = \"Theme\"; ObjectID = \"8z7-Jc-agP\";",
"extractionState" : "extracted_with_value",

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "Ações Rápidas";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "O caminho absoluto do arquivo solto.";

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "Hızlı Eylemler";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "Bırakılan dosya mutlak yolu.";

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "快速操作";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "拖拽的文件绝对路径";

View File

@ -829,6 +829,11 @@
/* MARK: Quick Action Bar */
"Quick Actions" = "快速動作";
/* MARK: FileDropItem */
// Descriptions about variables in the file drop feature
"The dropped file absolute path." = "拖拽的檔案絕對路徑";