Feature Request: small, tiny countdown on the user input “Yes”

Hey all,

What do you think about this? A little countdown timer so users don’t have to press 'yes' all the time. Ample time to give users some time to assess if everything is fine; it can be a 5 or 10-second timer as needed.

A timer is certainly doable. Can you give us a bit more context?

In my opinion the simplest way is to add a timeout (click on Set Action Timeout) to the Prompt action using the menu I'm showing you in the image below, which you get by clicking on the cogwheel. Then, by inserting the Prompt action inside a Try action, if the action times out, you can execute special code to handle the timeout in the location where I placed the comment. If there is no timeout, the Try action simply completes and goes to the next action. I can't complete this example because I don't know what you want done for each button. If "No" cancels your macro, then it's easy - no extra code is required.

4 Likes

Hi, @RazMastero. As @noisneil mentioned and @Airy demonstrated, this is very doable.

@Airy leveraged the Prompt For User Input timeout and provided a nice, simple solution.


Albeit not as simple, here's a method that will display the countdown.

Download: Countdown Prompt.kmmacros (8.7 KB)

Macro-Image


This method also required configuration of the Prompt For User Input gear settings.


If you want something with more flexibility, the sky is the limit with the Custom HTML Prompt action.

A macro I recently updated (Insert Text Into Restricted Field) includes a countdown window with three buttons: Cancel , Reset , and Restart ; the first will cancel the macro (esc will do the same), the second will restart the countdown, and the third will restart the macro.

1 Like

Nice work and also thanks for sharing your HTML prompt.

Countdown

1 Like

Similar to @Airy's method -- but too "flickery" for my taste. Certainly grabs your attention, though!

Countdown Dialog.kmmacros (4.0 KB)

Image

2 Likes

I have an amazing new method but I'm still having some problems with the logic. One problem is that I need to find a way to set IDLE() to zero by doing something innocuous that won't impact whatever app is up front. But sending character keys is not likely to work reliably since it may interfere with the app. Nor do I want to visibly move the mouse, even one pixel (which may not even work if the mouse is already on the edge of the screen.) Can you think of a way to set IDLE() to zero without interfering with the user's frontmost app? Maybe click an unused mouse button?

Yeah the flickering is certainly attention grabbing. I thought you had fixed the flickering but just read it wrong. Very cool you were able to put it in the button.

Try a "Move mouse" that doesn't change the pointer position:

image

Works here!

1 Like

I call that flicker a feature, @Nige_S. :grinning: You wouldn't want a macro user to miss the countdown, right?


Recently I revised my go-to countdown timer to momentarily flash a green background when it first appears (or is reset) because I want to draw attention to it. (Call it a colorful flicker. :grinning:)

Also when it's about to expire, it's background changes to yellow. That gives me plently of time to click the Reset button if I need more time.

Timer


As I'm sure you know, stuff like this is remarkably easy with the Custom HTML Prompt action:

( Custom HTLM Prompt configuration )
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <style type="text/css">
    body {
      margin-top: 5px;
      margin-bottom: 0;
      background: #ebebeb;
      font-family: Sans-Serif;
      color: red;
    }
    h1 {
      margin-top: 0;
      margin-bottom: 2px;
      font-size: 24px;
    }
    @keyframes FlashGreen {
      to {
        background-color: lightgreen;
        color: black;
      }
    }
    .color_invert {
      animation-name: FlashGreen;
      animation-duration: 0.5s;
      animation-timing-function: ease-out;
      animation-delay: 0;
      animation-iteration-count: 1;
      animation-direction: alternate-reverse;
    }
    .yellow_background {
      background-color: yellow;
      color: red;
    }
  </style>
</head>
<body data-kmwindowid="Archive MD5 Tamper" data-kmwindow="0, 0,250,60">
  <div>
    <h1 id="countdown" style="text-align: center; vertical-align: middle;">Time</h1>
  </div>
  <form method="post">
    <div style="text-align: center; vertical-align: middle;">
      <button name="CANCEL" type="button" onclick="DismissAndCancelMacro()">Cancel</button>&nbsp;&nbsp;
      <button name="RESET" type="button" onclick="ResetCountdown()">Reset</button>&nbsp;&nbsp;
      <button name="RESTART" type="button" onclick="DismissAndRestartMacro()">Restart</button>
    </div>
  </form>
  <script>
    StartCountdown();

    function KMInit() {
      document.title = window.KeyboardMaestro.GetVariable("local_CountdownTitle");

      var timeinterval = setInterval(function() {
        SetCountdown();
        if (!SetCountdown()) {
          clearInterval(timeinterval);
          window.KeyboardMaestro.SetVariable("local_CountdownStatus", 'expired');
          window.close();
        }
      }, 1000);
    }

    function StartCountdown() {
      window.endTime = Date.parse(new Date()) + window.KeyboardMaestro.GetVariable("local__Wait") * 1000;
      SetCountdown();
      TimedUseOfBodyClass('color_invert', 400);
      document.body.classList.remove('yellow_background');
    }

    function DismissAndCancelMacro() {
      window.KeyboardMaestro.Cancel('Cancel');
    }

    function ResetCountdown() {
      StartCountdown();
      TimedUseOfBodyClass('color_invert', 400);
      document.body.classList.remove('yellow_background');
    }

    function DismissAndRestartMacro() {
      window.KeyboardMaestro.Submit('Restart');
    }

    function SetCountdown() {
      var countdown = document.getElementById("countdown");
      var t = window.endTime - Date.parse(new Date());
      if (t < 0) {
        t = 0;
      }
      var seconds = Math.floor((t / 1000) % 60);
      var minutes = Math.floor((t / 1000 / 60) % 60);
      countdown.innerHTML = minutes + ':' + ("0" + seconds).slice(-2);

      if (t <= 5000 && t > 0) {
        document.body.classList.add('yellow_background');
      }

      return t > 0;
    }

    function TimedUseOfBodyClass(theClass, milliseconds) {
      document.body.classList.add(theClass);
      window.setTimeout(function() {
        document.body.classList.remove(theClass);
      }, milliseconds);
    }

    document.addEventListener("keydown", function(event) {
      if (event.key === "Escape") {
        DismissAndCancelMacro();
      }
    });

  </script>
</body>
</html>


Attribution: The HTML/JavaScript code was adapted from: Simplistic Countdown Timer, by @troy. In turn, that code was adapted from Keyboard Maestro Actions that @perternlewis supplied in this post.

Wow, I didn't think "moving the mouse zero pixels" would count as an action. Thanks. I had a convoluted algorithm that moved the mouse right if it was on an odd numbered pixel and left if it was on an even pixel. But that may not work on screen edges.

I do love it when someone follows "remarkably easy" with 100+ lines of code :wink:

4 Likes

I don't count the HTML, CSS, or whitespace, so I'd call it 40+ lines (of JavaScript). :sweat_smile:

@Nige_S, I've seen a fair amount of your AppleScript. For me, none of that is "remarkably easy". Touché! :grin:

2 Likes