diff --git a/resources/l10n/Localizable.strings b/resources/l10n/Localizable.strings index 5b4ada37..ed5a5865 100644 --- a/resources/l10n/Localizable.strings +++ b/resources/l10n/Localizable.strings @@ -611,6 +611,9 @@ /* No comment provided by engineer. */ "Window Title" = "Window Title"; +/* No comment provided by engineer. */ +"Window title contains" = "Window title contains"; + /* No comment provided by engineer. */ "Windows" = "Windows"; diff --git a/src/logic/MacroPreferences.swift b/src/logic/MacroPreferences.swift index 56c3b8e2..2adde7e8 100644 --- a/src/logic/MacroPreferences.swift +++ b/src/logic/MacroPreferences.swift @@ -526,12 +526,14 @@ enum BlacklistHidePreference: String/* required for jsonEncode */, CaseIterable, case none = "0" case always = "1" case whenNoOpenWindow = "2" + case windowTitleContains = "3" var localizedString: LocalizedString { switch self { case .none: return "" case .always: return NSLocalizedString("Always", comment: "") case .whenNoOpenWindow: return NSLocalizedString("When no open window", comment: "") + case .windowTitleContains: return NSLocalizedString("Window title contains", comment: "") } } } diff --git a/src/logic/Preferences.swift b/src/logic/Preferences.swift index 38b0cbfc..afdad15a 100644 --- a/src/logic/Preferences.swift +++ b/src/logic/Preferences.swift @@ -329,4 +329,5 @@ struct BlacklistEntry: Codable { var bundleIdentifier: String var hide: BlacklistHidePreference var ignore: BlacklistIgnorePreference + var windowTitleContains: String? } diff --git a/src/logic/Windows.swift b/src/logic/Windows.swift index 5e6ff37c..eeb185f8 100644 --- a/src/logic/Windows.swift +++ b/src/logic/Windows.swift @@ -163,12 +163,27 @@ class Windows { } } + private static func shouldHideWindow(_ window: Window, _ entry: BlacklistEntry) -> Bool { + switch entry.hide { + case .none: + return false + case .always: + return true + case .whenNoOpenWindow: + return window.isWindowlessApp + case .windowTitleContains: + guard let titleFilter = entry.windowTitleContains, !titleFilter.isEmpty else { + return false + } + return window.title.contains(titleFilter) + } + } + private static func refreshIfWindowShouldBeShownToTheUser(_ window: Window) { window.shouldShowTheUser = !(window.application.bundleIdentifier.flatMap { id in Preferences.blacklist.contains { - id.hasPrefix($0.bundleIdentifier) && - ($0.hide == .always || (window.isWindowlessApp && $0.hide != .none)) + id.hasPrefix($0.bundleIdentifier) && shouldHideWindow(window, $0) } } ?? false) && !(Preferences.appsToShow[App.app.shortcutIndex] == .active && window.application.pid != Applications.frontmostPid) && diff --git a/src/ui/generic-components/TableView.swift b/src/ui/generic-components/TableView.swift index 03ef614e..f9c4c187 100644 --- a/src/ui/generic-components/TableView.swift +++ b/src/ui/generic-components/TableView.swift @@ -86,6 +86,7 @@ class TableView: NSTableView { addHeaders([ NSLocalizedString("App (BundleID starting with)", comment: ""), String(format: NSLocalizedString("Hide in %@", comment: "%@ is AltTab"), App.name), + NSLocalizedString("Window title contains", comment: ""), NSLocalizedString("Ignore shortcuts when active", comment: "") ]) reloadData() @@ -93,7 +94,7 @@ class TableView: NSTableView { func insertRow(_ bundleId: String) { if !(items.contains { $0.bundleIdentifier == bundleId }) { - items.append(BlacklistEntry(bundleIdentifier: bundleId, hide: .always, ignore: .none)) + items.append(BlacklistEntry(bundleIdentifier: bundleId, hide: .always, ignore: .none, windowTitleContains: nil)) insertRows(at: [numberOfRows]) savePreferences() } @@ -115,7 +116,7 @@ class TableView: NSTableView { column.headerToolTip = header column.headerCell = TableHeaderCell(header) if i == 0 { - column.width = 206 + column.width = 180 } addTableColumn(column) } @@ -127,6 +128,8 @@ class TableView: NSTableView { items[row].bundleIdentifier = LabelAndControl.getControlValue(control, nil)! } else if colId == "col2" { items[row].hide = BlacklistHidePreference.allCases[Int(LabelAndControl.getControlValue(control, nil)!)!] + } else if colId == "col3" { + items[row].windowTitleContains = LabelAndControl.getControlValue(control, nil) } else { items[row].ignore = BlacklistIgnorePreference.allCases[Int(LabelAndControl.getControlValue(control, nil)!)!] } @@ -154,6 +157,23 @@ class TableView: NSTableView { return parent } + private func titleText(_ item: BlacklistEntry) -> NSView { + let text = TextField(item.windowTitleContains ?? "") + text.isEditable = true + text.allowsExpansionToolTips = true + text.drawsBackground = false + text.isBordered = false + text.lineBreakMode = .byTruncatingTail + text.usesSingleLineMode = true + text.cell!.sendsActionOnEndEditing = true + text.onAction = { self.wasUpdated("col3", $0) } + let parent = NSView() + parent.addSubview(text) + text.centerYAnchor.constraint(equalTo: parent.centerYAnchor).isActive = true + text.widthAnchor.constraint(equalTo: parent.widthAnchor).isActive = true + return parent + } + private func dropdown(_ item: BlacklistEntry, _ colId: String) -> NSView { let isHidePref = colId == "col2" let button = NSPopUpButton() @@ -186,7 +206,14 @@ extension TableView: NSTableViewDataSource { extension TableView: NSTableViewDelegate { func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let item = items[row] - return tableColumn!.identifier.rawValue == "col1" ? text(item) : dropdown(item, tableColumn!.identifier.rawValue) + let colId = tableColumn!.identifier.rawValue + if colId == "col1" { + return text(item) + } else if colId == "col3" { + return titleText(item) + } else { + return dropdown(item, colId) + } } }