Skip to content

Commit

Permalink
Add option to type a custom date format
Browse files Browse the repository at this point in the history
  • Loading branch information
pakerwreah committed Nov 4, 2022
1 parent 1277d4d commit 7440781
Show file tree
Hide file tree
Showing 20 changed files with 194 additions and 45 deletions.
4 changes: 2 additions & 2 deletions Calendr.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.6.6;
MARKETING_VERSION = 1.6.7;
PRODUCT_BUNDLE_IDENTIFIER = br.paker.Calendr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Calendr/Config/Calendr-Bridging-Header.h";
Expand All @@ -1157,7 +1157,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.6.6;
MARKETING_VERSION = 1.6.7;
PRODUCT_BUNDLE_IDENTIFIER = br.paker.Calendr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Calendr/Config/Calendr-Bridging-Header.h";
Expand Down
2 changes: 2 additions & 0 deletions Calendr/Assets/Accessibility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ enum Accessibility {

enum General {
static let view = "settings_general_view"
static let dateFormatDropdown = "settings_general_date_format_dropdown"
static let dateFormatInput = "settings_general_date_format_input"
}

enum Calendars {
Expand Down
6 changes: 2 additions & 4 deletions Calendr/Assets/Generated/Strings.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ internal enum Strings {
internal enum MenuBar {
/// Date format
internal static let dateFormat = Strings.tr("Localizable", "settings.menu_bar.date_format", fallback: "Date format")
/// Custom
internal static let dateFormatCustom = Strings.tr("Localizable", "settings.menu_bar.date_format_custom", fallback: "Custom")
/// Shorten if 'notch' is present
internal static let nextEventDetectNotch = Strings.tr("Localizable", "settings.menu_bar.next_event_detect_notch", fallback: "Shorten if 'notch' is present")
/// Width
Expand All @@ -117,10 +119,6 @@ internal enum Strings {
internal static let showIcon = Strings.tr("Localizable", "settings.menu_bar.show_icon", fallback: "Show icon")
/// Show next event
internal static let showNextEvent = Strings.tr("Localizable", "settings.menu_bar.show_next_event", fallback: "Show next event")
internal enum DateFormat {
/// Configurable in System Preferences
internal static let info = Strings.tr("Localizable", "settings.menu_bar.date_format.info", fallback: "Configurable in System Preferences")
}
}
internal enum Tab {
/// About
Expand Down
2 changes: 1 addition & 1 deletion Calendr/Assets/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"settings.menu_bar.show_icon" = "Zeige Icon";
"settings.menu_bar.show_date" = "Zeige Datum";
"settings.menu_bar.date_format" = "Datumsformat";
"settings.menu_bar.date_format.info" = "In Systemeinstellungen konfigurieren";
"settings.menu_bar.date_format_custom" = "Benutzerdefiniert";
"settings.menu_bar.show_next_event" = "Zeige nächsten Termin";
"settings.menu_bar.next_event_length" = "Breite";
"settings.menu_bar.next_event_detect_notch" = "Kürzen, wenn 'notch' vorhanden ist";
Expand Down
2 changes: 1 addition & 1 deletion Calendr/Assets/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"settings.menu_bar.show_icon" = "Show icon";
"settings.menu_bar.show_date" = "Show date";
"settings.menu_bar.date_format" = "Date format";
"settings.menu_bar.date_format.info" = "Configurable in System Preferences";
"settings.menu_bar.date_format_custom" = "Custom";
"settings.menu_bar.show_next_event" = "Show next event";
"settings.menu_bar.next_event_length" = "Width";
"settings.menu_bar.next_event_detect_notch" = "Shorten if 'notch' is present";
Expand Down
2 changes: 1 addition & 1 deletion Calendr/Assets/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"settings.menu_bar.show_icon" = "Mostrar icono";
"settings.menu_bar.show_date" = "Mostrar fecha";
"settings.menu_bar.date_format" = "Formato de fecha";
"settings.menu_bar.date_format.info" = "Configurable en Preferencias del Sistema";
"settings.menu_bar.date_format_custom" = "Personalizado";
"settings.menu_bar.show_next_event" = "Mostrar próximo evento";
"settings.menu_bar.next_event_length" = "Ancho";
"settings.menu_bar.next_event_detect_notch" = "Acortar si 'notch' está presente";
Expand Down
2 changes: 1 addition & 1 deletion Calendr/Assets/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"settings.menu_bar.show_icon" = "Afficher l'icône";
"settings.menu_bar.show_date" = "Afficher la date";
"settings.menu_bar.date_format" = "Format de date";
"settings.menu_bar.date_format.info" = "Configurable dans les Préférences Système";
"settings.menu_bar.date_format_custom" = "Personnalisé";
"settings.menu_bar.show_next_event" = "Afficher prochain événement";
"settings.menu_bar.next_event_length" = "Largeur";
"settings.menu_bar.next_event_detect_notch" = "Raccourcir si 'notch' est présent";
Expand Down
2 changes: 1 addition & 1 deletion Calendr/Assets/pt.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"settings.menu_bar.show_icon" = "Mostrar ícone";
"settings.menu_bar.show_date" = "Mostrar data";
"settings.menu_bar.date_format" = "Formato de data";
"settings.menu_bar.date_format.info" = "Configurável nas Preferências do Sistema";
"settings.menu_bar.date_format_custom" = "Personalizado";
"settings.menu_bar.show_next_event" = "Mostrar próximo evento";
"settings.menu_bar.next_event_length" = "Largura";
"settings.menu_bar.next_event_detect_notch" = "Encurtar se 'notch' estiver presente";
Expand Down
26 changes: 26 additions & 0 deletions Calendr/Main/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,31 @@ class AppDelegate: NSObject, NSApplicationDelegate {
userDefaults: .standard,
notificationCenter: .default
)

setUpKeyboard()
}

// 🔨 This will not be visible, but it allows us to use basic commands in text fields
private func setUpKeyboard() {

let mainMenu = NSMenu(title: "MainMenu")
let menuItem = mainMenu.addItem(withTitle: "", action: nil, keyEquivalent: "")

let submenu = NSMenu()
submenu.addItem(withTitle: "Close Window", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w")
submenu.addItem(withTitle: "Undo", action: #selector(EditMenuActions.undo(_:)), keyEquivalent: "z")
submenu.addItem(withTitle: "Redo", action: #selector(EditMenuActions.redo(_:)), keyEquivalent: "Z")
submenu.addItem(withTitle: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x")
submenu.addItem(withTitle: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c")
submenu.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v")
submenu.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")

mainMenu.setSubmenu(submenu, for: menuItem)
NSApp.mainMenu = mainMenu
}
}

@objc private protocol EditMenuActions {
func redo(_ sender: AnyObject)
func undo(_ sender: AnyObject)
}
3 changes: 2 additions & 1 deletion Calendr/Main/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,8 @@ class MainViewController: NSViewController {

NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event -> NSEvent? in

if event.keyCode == 53, let vc = self?.presentedViewControllers?.last {
if let vc = self?.presentedViewControllers?.last {
guard event.keyCode == 53 else { return event }
self?.dismiss(vc)
return nil
}
Expand Down
19 changes: 15 additions & 4 deletions Calendr/MenuBar/StatusItemViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,20 @@ class StatusItemViewModel {
.notification(NSLocale.currentLocaleDidChangeNotification)
.void()

let dateFormatterObservable = settings.statusItemDateStyle
let dateFormatterObservable = Observable
.combineLatest(settings.statusItemDateStyle, settings.statusItemDateFormat)
.repeat(when: localeChangeObservable)
.map { dateStyle in
DateFormatter(calendar: dateProvider.calendar).with(style: dateStyle)
.map { style, format in

let formatter = DateFormatter(calendar: dateProvider.calendar)

if style.isCustom {
formatter.dateFormat = format
} else {
formatter.dateStyle = style
}

return formatter
}

let shouldCompact = Observable
Expand Down Expand Up @@ -74,7 +84,8 @@ class StatusItemViewModel {
if title.length > 0 {
title.append(NSAttributedString(string: " "))
}
title.append(NSAttributedString(string: dateFormatter.string(from: date)))
let text = dateFormatter.string(from: date)
title.append(NSAttributedString(string: text.isEmpty ? "???" : text))
}

return title
Expand Down
35 changes: 29 additions & 6 deletions Calendr/Settings/GeneralSettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class GeneralSettingsViewController: NSViewController {
private let fadePastEventsRadio = Radio(title: Strings.Settings.Events.Finished.fade)
private let hidePastEventsRadio = Radio(title: Strings.Settings.Events.Finished.hide)
private let dateFormatDropdown = Dropdown()
private let dateFormatTextField = NSTextField()

private let nextEventLengthSlider = NSSlider.make(minValue: 10, maxValue: 30)
private let transparencySlider = NSSlider.make(minValue: 0, maxValue: 5)
Expand All @@ -46,6 +47,9 @@ class GeneralSettingsViewController: NSViewController {

view.setAccessibilityElement(true)
view.setAccessibilityIdentifier(Accessibility.Settings.General.view)

dateFormatDropdown.setAccessibilityIdentifier(Accessibility.Settings.General.dateFormatDropdown)
dateFormatTextField.setAccessibilityIdentifier(Accessibility.Settings.General.dateFormatInput)
}

override func loadView() {
Expand Down Expand Up @@ -89,13 +93,12 @@ class GeneralSettingsViewController: NSViewController {
])
.with(orientation: .vertical)

dateFormatTextField.placeholderString = viewModel.dateFormatPlaceholder

let dateFormat = NSStackView(views: [
Label(text: "\(Strings.Settings.MenuBar.dateFormat):"),
dateFormatDropdown,
Label(
text: " \(Strings.Settings.MenuBar.DateFormat.info)",
font: .systemFont(ofSize: 10, weight: .light)
)
dateFormatTextField
])
.with(orientation: .vertical)

Expand Down Expand Up @@ -240,7 +243,7 @@ class GeneralSettingsViewController: NSViewController {
DateStyle(rawValue: UInt(dropdown.indexOfSelectedItem + 1)) ?? .none
},
setter: { (dropdown: NSPopUpButton, style: DateStyle) in
dropdown.selectItem(at: Int(style.rawValue) - 1)
dropdown.selectItem(at: (style.isCustom ? dropdown.numberOfItems : Int(style.rawValue)) - 1)
}
)

Expand All @@ -250,14 +253,34 @@ class GeneralSettingsViewController: NSViewController {
.disposed(by: disposeBag)

Observable.combineLatest(
viewModel.dateFormatOptions, viewModel.statusItemDateStyle
viewModel.dateStyleOptions, viewModel.statusItemDateStyle
)
.bind { [dateFormatDropdown] options, dateStyle in
dateFormatDropdown.removeAllItems()
dateFormatDropdown.addItems(withTitles: options)
dateFormatStyle.onNext(dateStyle)
}
.disposed(by: disposeBag)

viewModel.isDateFormatInputVisible
.map(!)
.bind(to: dateFormatTextField.rx.isHidden)
.disposed(by: disposeBag)

viewModel.isDateFormatInputVisible
.map(true)
.bind(to: view.rx.needsLayout)
.disposed(by: disposeBag)

dateFormatTextField.rx.text
.skip(1)
.skipNil()
.bind(to: viewModel.statusItemDateFormatObserver)
.disposed(by: disposeBag)

viewModel.statusItemDateFormat
.bind(to: dateFormatTextField.rx.text)
.disposed(by: disposeBag)
}

private func bind(control: NSButton, observable: Observable<Bool>, observer: AnyObserver<Bool>) {
Expand Down
6 changes: 6 additions & 0 deletions Calendr/Settings/Prefs+UserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum Prefs {
static let statusItemIconEnabled = "status_item_icon_enabled"
static let statusItemDateEnabled = "status_item_date_enabled"
static let statusItemDateStyle = "status_item_date_style"
static let statusItemDateFormat = "status_item_date_format"
static let showEventStatusItem = "show_event_status_item"
static let eventStatusItemLength = "event_status_item_length"
static let eventStatusItemDetectNotch = "event_status_item_detect_notch"
Expand Down Expand Up @@ -44,6 +45,11 @@ extension UserDefaults {
get { UInt(integer(forKey: Prefs.statusItemDateStyle)) }
set { set(newValue, forKey: Prefs.statusItemDateStyle) }
}

@objc dynamic var statusItemDateFormat: String {
get { string(forKey: Prefs.statusItemDateFormat) ?? "" }
set { set(newValue, forKey: Prefs.statusItemDateFormat) }
}

@objc dynamic var showEventStatusItem: Bool {
get { bool(forKey: Prefs.showEventStatusItem) }
Expand Down
38 changes: 29 additions & 9 deletions Calendr/Settings/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import Cocoa
import RxSwift

typealias DateStyle = DateFormatter.Style

extension DateStyle {
static let options: [Self] = [.short, .medium, .long, .full]
var isCustom: Bool { !Self.options.contains(self) }
}

typealias PopoverMaterial = NSVisualEffectView.Material

extension PopoverMaterial {
Expand All @@ -29,6 +35,7 @@ protocol StatusItemSettings {
var showStatusItemIcon: Observable<Bool> { get }
var showStatusItemDate: Observable<Bool> { get }
var statusItemDateStyle: Observable<DateStyle> { get }
var statusItemDateFormat: Observable<String> { get }
var eventStatusItemDetectNotch: Observable<Bool> { get }
}

Expand Down Expand Up @@ -59,6 +66,7 @@ class SettingsViewModel: StatusItemSettings, NextEventSettings, CalendarSettings
let toggleStatusItemIcon: AnyObserver<Bool>
let toggleStatusItemDate: AnyObserver<Bool>
let statusItemDateStyleObserver: AnyObserver<DateStyle>
let statusItemDateFormatObserver: AnyObserver<String>
let toggleEventStatusItem: AnyObserver<Bool>
let eventStatusItemLengthObserver: AnyObserver<Int>
let toggleEventStatusItemDetectNotch: AnyObserver<Bool>
Expand All @@ -70,9 +78,13 @@ class SettingsViewModel: StatusItemSettings, NextEventSettings, CalendarSettings
let calendarScalingObserver: AnyObserver<Double>

// Observables
var showStatusItemIcon: Observable<Bool>
var showStatusItemDate: Observable<Bool>
var statusItemDateStyle: Observable<DateStyle>
let showStatusItemIcon: Observable<Bool>
let showStatusItemDate: Observable<Bool>
let statusItemDateStyle: Observable<DateStyle>
let dateStyleOptions: Observable<[String]>
let statusItemDateFormat: Observable<String>
let dateFormatPlaceholder = "E d MMM YYYY"
let isDateFormatInputVisible: Observable<Bool>
let showEventStatusItem: Observable<Bool>
let eventStatusItemLength: Observable<Int>
let eventStatusItemDetectNotch: Observable<Bool>
Expand All @@ -84,8 +96,6 @@ class SettingsViewModel: StatusItemSettings, NextEventSettings, CalendarSettings
let popoverMaterial: Observable<PopoverMaterial>
let calendarScaling: Observable<Double>

let dateFormatOptions: Observable<[String]>

init(
dateProvider: DateProviding,
userDefaults: UserDefaults,
Expand All @@ -95,7 +105,8 @@ class SettingsViewModel: StatusItemSettings, NextEventSettings, CalendarSettings
userDefaults.register(defaults: [
Prefs.statusItemIconEnabled: true,
Prefs.statusItemDateEnabled: true,
Prefs.statusItemDateStyle: 1,
Prefs.statusItemDateStyle: DateStyle.short.rawValue,
Prefs.statusItemDateFormat: dateFormatPlaceholder,
Prefs.showEventStatusItem: false,
Prefs.eventStatusItemLength: 18,
Prefs.eventStatusItemDetectNotch: false,
Expand All @@ -110,6 +121,7 @@ class SettingsViewModel: StatusItemSettings, NextEventSettings, CalendarSettings
toggleStatusItemIcon = userDefaults.rx.observer(for: \.statusItemIconEnabled)
toggleStatusItemDate = userDefaults.rx.observer(for: \.statusItemDateEnabled)
statusItemDateStyleObserver = userDefaults.rx.observer(for: \.statusItemDateStyle).mapObserver(\.rawValue)
statusItemDateFormatObserver = userDefaults.rx.observer(for: \.statusItemDateFormat)
toggleEventStatusItem = userDefaults.rx.observer(for: \.showEventStatusItem)
eventStatusItemLengthObserver = userDefaults.rx.observer(for: \.eventStatusItemLength)
toggleEventStatusItemDetectNotch = userDefaults.rx.observer(for: \.eventStatusItemDetectNotch)
Expand All @@ -131,6 +143,7 @@ class SettingsViewModel: StatusItemSettings, NextEventSettings, CalendarSettings
showStatusItemIcon = statusItemIconAndDate.map(\.0)
showStatusItemDate = statusItemIconAndDate.map(\.1)
statusItemDateStyle = userDefaults.rx.observe(\.statusItemDateStyle).map { DateStyle(rawValue: $0) ?? .none }
statusItemDateFormat = userDefaults.rx.observe(\.statusItemDateFormat)
showEventStatusItem = userDefaults.rx.observe(\.showEventStatusItem)
eventStatusItemLength = userDefaults.rx.observe(\.eventStatusItemLength)
eventStatusItemDetectNotch = userDefaults.rx.observe(\.eventStatusItemDetectNotch)
Expand All @@ -141,22 +154,29 @@ class SettingsViewModel: StatusItemSettings, NextEventSettings, CalendarSettings
popoverTransparency = userDefaults.rx.observe(\.transparencyLevel)
calendarScaling = userDefaults.rx.observe(\.calendarScaling)

dateFormatOptions = notificationCenter.rx.notification(NSLocale.currentLocaleDidChangeNotification)
dateStyleOptions = notificationCenter.rx.notification(NSLocale.currentLocaleDidChangeNotification)
.void()
.startWith(())
.map {
let dateFormatter = DateFormatter(calendar: dateProvider.calendar)
var options: [String] = []

for i: UInt in 1...4 {
dateFormatter.dateStyle = DateStyle(rawValue: i) ?? .none
for i: UInt in DateStyle.options.map(\.rawValue) {
dateFormatter.dateStyle = .init(rawValue: i) ?? .none
options.append(dateFormatter.string(from: dateProvider.now))
}

options.append("\(Strings.Settings.MenuBar.dateFormatCustom)...")

return options
}
.share(replay: 1)

isDateFormatInputVisible = statusItemDateStyle
.map(\.isCustom)
.distinctUntilChanged()
.share(replay: 1)

popoverMaterial = popoverTransparency.map(PopoverMaterial.init(transparency:))
}
}
Loading

0 comments on commit 7440781

Please sign in to comment.