numist / Switch

A window-based context switcher for the Mac
157 stars 17 forks source link

Display interface if raise takes too long. #125

Closed numist closed 3 years ago

numist commented 9 years ago

Otherwise the behaviour introduced by https://github.com/numist/Switch/pull/124 won't feel very good on slower systems.

numist commented 9 years ago

I already have a patch that can be adapted to fix this (below), but what I really want is for the state machine to request different lengths of time, because the normal UI delay should be short (on the order of 0.2s) and the delay for impatience waiting for a window to raise should be longer (at a guess, on the order of 0.7s). Shouldn't be much extra work.

diff --git a/Switch/SWCoreWindowService.m b/Switch/SWCoreWindowService.m
index 3560dfc..d685b04 100644
--- a/Switch/SWCoreWindowService.m
+++ b/Switch/SWCoreWindowService.m
@@ -368,11 +368,8 @@ static int const kScrollThreshold = 50;
         return;
     }

-    // Do not show the interface if the state machine has an action pending.
-    if (!self.stateMachine.pendingSwitch) {
-        [self.stateMachine displayTimerCompleted];
-        self.displayTimer = nil;
-    }
+    [self.stateMachine displayTimerCompleted];
+    self.displayTimer = nil;
 }

 - (void)private_showInterface;
diff --git a/Switch/SWStateMachine.m b/Switch/SWStateMachine.m
index 55c0330..3443fe3 100644
--- a/Switch/SWStateMachine.m
+++ b/Switch/SWStateMachine.m
@@ -35,6 +35,7 @@

 @property (nonatomic, readwrite, assign) _Bool invoked;
 @property (nonatomic, readwrite, assign, getter=wantsDisplayTimer) _Bool displayTimer;
+@property (nonatomic, readwrite, assign) _Bool displayTimerFired;
 @property (nonatomic, readwrite, assign) _Bool pendingSwitch;
 @property (nonatomic, readwrite, assign) _Bool windowListLoaded;
 @property (nonatomic, readwrite, assign, getter=wantsWindowListUpdates) _Bool windowListUpdates;
@@ -139,8 +140,9 @@
         if (!Check(!self.displayTimer)) {
             [delegate stateMachineWantsDisplayTimerInvalidated:self];
         }
-        [delegate stateMachineWantsDisplayTimerStarted:self];
+        self.displayTimerFired = false;
         self.displayTimer = true;
+        [delegate stateMachineWantsDisplayTimerStarted:self];

         Check(!self.selector);
         self.selector = [SWSelector new];
@@ -158,6 +160,7 @@
             [delegate stateMachineWantsDisplayTimerInvalidated:self];
             self.displayTimer = false;
         }
+        self.displayTimerFired = false;

         self.selector = nil;
         self.pendingSwitch = false;
@@ -168,6 +171,7 @@
 {
     if (interfaceVisible == self.interfaceVisible) { return; }
     self->_interfaceVisible = interfaceVisible;
+    StateLog(interfaceVisible ? @"State machine showing interface" : @"State machine hiding interface");

     if (interfaceVisible) {
         [self private_adjustSelector];
@@ -184,7 +188,20 @@
 - (void)displayTimerCompleted;
 {
     StateLog(@"State machine display timer completed");
-    self.displayTimer = false;
+
+    _Bool restartTimer = false;
+    if (self.pendingSwitch && !self.displayTimerFired) {
+        restartTimer = true;
+    } else {
+        self.displayTimer = false;
+    }
+    
+    self.displayTimerFired = true;
+    
+    if (restartTimer) {
+        id<SWStateMachineDelegate> delegate = self.delegate;
+        [delegate stateMachineWantsDisplayTimerStarted:self];
+    }
 }

 #pragma mark - Keyboard interactions
diff --git a/SwitchTests/SWStateMachineTests.m b/SwitchTests/SWStateMachineTests.m
index 5be359b..1c409ab 100644
--- a/SwitchTests/SWStateMachineTests.m
+++ b/SwitchTests/SWStateMachineTests.m
@@ -1273,9 +1273,13 @@ static SWWindow *(^rwg)() = ^{
     [self stateMachineExpectRaise:^{
         [self _keyReleased];
     }];
-
+    
     // Remember: if (!showingUI) expectWantsDisplay
     if (!self.stateMachineUnderTest.interfaceVisible) {
+        [self stateMachineExpectDisplayTimerStart:^{
+            [self _timer];
+        }];
+        
         [self stateMachineExpectShowInterface:true block:^{
             [self _timer];
         }];
@@ -1296,6 +1300,10 @@ static SWWindow *(^rwg)() = ^{

     // Remember: if (!showingUI) expectWantsDisplay
     if (!self.stateMachineUnderTest.interfaceVisible) {
+        [self stateMachineExpectDisplayTimerStart:^{
+            [self _timer];
+        }];
+        
         [self stateMachineExpectShowInterface:true block:^{
             [self _timer];
         }];
@@ -1311,6 +1319,10 @@ static SWWindow *(^rwg)() = ^{

     [self _keyReleased];

+    [self stateMachineExpectDisplayTimerStart:^{
+        [self _timer];
+    }];
+    
     [self _timer];

     [self stateMachineExpectRaise:^{
numist commented 3 years ago

The approach in #139 is to be less complicated and leave retries in the hands of the user. This is probably for the best as the interface—in all its effort to be robust and helpful—tended to just get wedged by underlying system failures, which was even more annoying.