Merge pull request #2540 from danielpunkass/jalkut-skip-mode
Support a new secret user default…
This commit is contained in:
commit
b504c88948
@ -323,13 +323,16 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
|
|||||||
NSCursor.setHiddenUntilMouseMoves(true)
|
NSCursor.setHiddenUntilMouseMoves(true)
|
||||||
|
|
||||||
// TODO: handle search mode
|
// TODO: handle search mode
|
||||||
if timelineViewController.canGoToNextUnread() {
|
if timelineViewController.canGoToNextUnread(wrappingToTop: false) {
|
||||||
goToNextUnreadInTimeline()
|
goToNextUnreadInTimeline(wrappingToTop: false)
|
||||||
}
|
}
|
||||||
else if sidebarViewController.canGoToNextUnread() {
|
else if sidebarViewController.canGoToNextUnread(wrappingToTop: true) {
|
||||||
sidebarViewController.goToNextUnread()
|
sidebarViewController.goToNextUnread(wrappingToTop: true)
|
||||||
if timelineViewController.canGoToNextUnread() {
|
|
||||||
goToNextUnreadInTimeline()
|
// If we ended up on the same timelineViewController, we may need to wrap
|
||||||
|
// around to the top of its contents.
|
||||||
|
if timelineViewController.canGoToNextUnread(wrappingToTop: true) {
|
||||||
|
goToNextUnreadInTimeline(wrappingToTop: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -995,13 +998,13 @@ private extension MainWindowController {
|
|||||||
|
|
||||||
// MARK: - Command Validation
|
// MARK: - Command Validation
|
||||||
|
|
||||||
func canGoToNextUnread() -> Bool {
|
func canGoToNextUnread(wrappingToTop wrapping: Bool = false) -> Bool {
|
||||||
|
|
||||||
guard let timelineViewController = currentTimelineViewController, let sidebarViewController = sidebarViewController else {
|
guard let timelineViewController = currentTimelineViewController, let sidebarViewController = sidebarViewController else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// TODO: handle search mode
|
// TODO: handle search mode
|
||||||
return timelineViewController.canGoToNextUnread() || sidebarViewController.canGoToNextUnread()
|
return timelineViewController.canGoToNextUnread(wrappingToTop: wrapping) || sidebarViewController.canGoToNextUnread(wrappingToTop: wrapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
func canMarkAllAsRead() -> Bool {
|
func canMarkAllAsRead() -> Bool {
|
||||||
@ -1188,14 +1191,14 @@ private extension MainWindowController {
|
|||||||
|
|
||||||
// MARK: - Misc.
|
// MARK: - Misc.
|
||||||
|
|
||||||
func goToNextUnreadInTimeline() {
|
func goToNextUnreadInTimeline(wrappingToTop wrapping: Bool) {
|
||||||
|
|
||||||
guard let timelineViewController = currentTimelineViewController else {
|
guard let timelineViewController = currentTimelineViewController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if timelineViewController.canGoToNextUnread() {
|
if timelineViewController.canGoToNextUnread(wrappingToTop: wrapping) {
|
||||||
timelineViewController.goToNextUnread()
|
timelineViewController.goToNextUnread(wrappingToTop: wrapping)
|
||||||
makeTimelineViewFirstResponder()
|
makeTimelineViewFirstResponder()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,15 +294,15 @@ protocol SidebarDelegate: class {
|
|||||||
|
|
||||||
// MARK: - Navigation
|
// MARK: - Navigation
|
||||||
|
|
||||||
func canGoToNextUnread() -> Bool {
|
func canGoToNextUnread(wrappingToTop wrapping: Bool = false) -> Bool {
|
||||||
if let _ = nextSelectableRowWithUnreadArticle() {
|
if let _ = nextSelectableRowWithUnreadArticle(wrappingToTop: wrapping) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func goToNextUnread() {
|
func goToNextUnread(wrappingToTop wrapping: Bool = false) {
|
||||||
guard let row = nextSelectableRowWithUnreadArticle() else {
|
guard let row = nextSelectableRowWithUnreadArticle(wrappingToTop: wrapping) else {
|
||||||
assertionFailure("goToNextUnread called before checking if there is a next unread.")
|
assertionFailure("goToNextUnread called before checking if there is a next unread.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -685,26 +685,42 @@ private extension SidebarViewController {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextSelectableRowWithUnreadArticle() -> Int? {
|
func rowIsExpandedFolder(_ row: Int) -> Bool {
|
||||||
|
if let node = nodeForRow(row), outlineView.isItemExpanded(node) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldSkipRow(_ row: Int) -> Bool {
|
||||||
|
let skipExpandedFolders = UserDefaults.standard.bool(forKey: "JalkutRespectFolderExpansionOnNextUnread")
|
||||||
|
|
||||||
// Skip group items, because they should never be selected.
|
// Skip group items, because they should never be selected.
|
||||||
|
// Skip expanded folders only if Jalkut's pref is enabled.
|
||||||
|
if rowIsGroupItem(row) || (skipExpandedFolders && rowIsExpandedFolder(row)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
let selectedRow = outlineView.selectedRow
|
func nextSelectableRowWithUnreadArticle(wrappingToTop wrapping: Bool = false) -> Int? {
|
||||||
let numberOfRows = outlineView.numberOfRows
|
let numberOfRows = outlineView.numberOfRows
|
||||||
var row = selectedRow + 1
|
let startRow = outlineView.selectedRow + 1
|
||||||
|
|
||||||
while (row < numberOfRows) {
|
let orderedRows: [Int]
|
||||||
if rowHasAtLeastOneUnreadArticle(row) && !rowIsGroupItem(row) {
|
if startRow == numberOfRows {
|
||||||
return row
|
// Last item is selected, so start at the beginning if we allow wrapping
|
||||||
}
|
orderedRows = wrapping ? Array(0..<numberOfRows) : []
|
||||||
row += 1
|
} else {
|
||||||
|
// Start at the selection and wrap around to the beginning
|
||||||
|
orderedRows = Array(startRow..<numberOfRows) + (wrapping ? Array(0..<startRow) : [])
|
||||||
}
|
}
|
||||||
|
|
||||||
row = 0
|
for row in orderedRows {
|
||||||
while (row <= selectedRow) {
|
// Skip group items, because they should never be selected.
|
||||||
if rowHasAtLeastOneUnreadArticle(row) && !rowIsGroupItem(row) {
|
if rowHasAtLeastOneUnreadArticle(row) && !shouldSkipRow(row) {
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
row += 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -549,7 +549,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
|
|||||||
tableView.scrollTo(row: ix)
|
tableView.scrollTo(row: ix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func goToNextUnread() {
|
func goToNextUnread(wrappingToTop wrapping: Bool = false) {
|
||||||
guard let ix = indexOfNextUnreadArticle() else {
|
guard let ix = indexOfNextUnreadArticle() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -558,15 +558,15 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
|
|||||||
tableView.scrollTo(row: ix)
|
tableView.scrollTo(row: ix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func canGoToNextUnread() -> Bool {
|
func canGoToNextUnread(wrappingToTop wrapping: Bool = false) -> Bool {
|
||||||
guard let _ = indexOfNextUnreadArticle() else {
|
guard let _ = indexOfNextUnreadArticle(wrappingToTop: wrapping) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func indexOfNextUnreadArticle() -> Int? {
|
func indexOfNextUnreadArticle(wrappingToTop wrapping: Bool = false) -> Int? {
|
||||||
return articles.rowOfNextUnreadArticle(tableView.selectedRow)
|
return articles.rowOfNextUnreadArticle(tableView.selectedRow, wrappingToTop: wrapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
func focus() {
|
func focus() {
|
||||||
|
@ -20,18 +20,21 @@ extension Array where Element == Article {
|
|||||||
return self[row]
|
return self[row]
|
||||||
}
|
}
|
||||||
|
|
||||||
func rowOfNextUnreadArticle(_ selectedRow: Int) -> Int? {
|
func orderedRowIndexes(fromIndex startIndex: Int, wrappingToTop wrapping: Bool) -> [Int] {
|
||||||
|
if startIndex >= self.count {
|
||||||
|
// Wrap around to the top if specified
|
||||||
|
return wrapping ? Array<Int>(0..<self.count) : []
|
||||||
|
} else {
|
||||||
|
// Start at the selection and wrap around to the beginning
|
||||||
|
return Array<Int>(startIndex..<self.count) + (wrapping ? Array<Int>(0..<startIndex) : [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func rowOfNextUnreadArticle(_ selectedRow: Int, wrappingToTop wrapping: Bool) -> Int? {
|
||||||
if isEmpty {
|
if isEmpty {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var rowIndex = selectedRow
|
for rowIndex in orderedRowIndexes(fromIndex: selectedRow + 1, wrappingToTop: wrapping) {
|
||||||
while(true) {
|
|
||||||
|
|
||||||
rowIndex = rowIndex + 1
|
|
||||||
if rowIndex >= count {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let article = articleAtRow(rowIndex)!
|
let article = articleAtRow(rowIndex)!
|
||||||
if !article.status.read {
|
if !article.status.read {
|
||||||
return rowIndex
|
return rowIndex
|
||||||
|
Loading…
x
Reference in New Issue
Block a user