Skip to content

Commit

Permalink
screen_utils: convert screen_get_yesno() to an awaitable
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxKellermann committed Sep 10, 2024
1 parent dc0ed2f commit 199b1cb
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 34 deletions.
6 changes: 3 additions & 3 deletions src/FileBrowserPage.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
#include "charset.hxx"
#include "mpdclient.hxx"
#include "filelist.hxx"
#include "screen_utils.hxx"
#include "screen_client.hxx"
#include "Command.hxx"
#include "Options.hxx"
#include "dialogs/YesNoDialog.hxx"
#include "ui/Bell.hxx"
#include "lib/fmt/ToSpan.hxx"
#include "util/UriUtil.hxx"
Expand Down Expand Up @@ -243,8 +243,8 @@ FileBrowserPage::HandleDelete(struct mpdclient &c)
snprintf(prompt, sizeof(prompt),
_("Delete playlist %s?"),
Utf8ToLocaleZ{GetUriFilename(mpd_playlist_get_path(playlist))}.c_str());
bool confirmed = screen_get_yesno(screen, prompt, false);
if (!confirmed)

if (co_await YesNoDialog{screen, prompt} != YesNoResult::YES)
co_return;

auto *connection = c.GetConnection();
Expand Down
84 changes: 84 additions & 0 deletions src/dialogs/YesNoDialog.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project

#include "YesNoDialog.hxx"
#include "ui/Bell.hxx"
#include "ui/Keys.hxx"
#include "ui/Options.hxx"
#include "ui/Window.hxx"
#include "Styles.hxx"
#include "i18n.h"

#include <ctype.h>

using std::string_view_literals::operator""sv;

void
YesNoDialog::OnLeave(const Window window) noexcept
{
noecho();
curs_set(0);

if (ui_options.enable_colors)
window.SetBackgroundStyle(Style::STATUS);
}

void
YesNoDialog::OnCancel() noexcept
{
SetResult(YesNoResult::CANCEL);
}

void
YesNoDialog::Paint(const Window window) const noexcept
{
if (ui_options.enable_colors)
window.SetBackgroundStyle(Style::INPUT);

SelectStyle(window, Style::STATUS_ALERT);
window.String({0, 0}, prompt);
window.String(" ["sv);
window.String(YES_TRANSLATION);
window.Char('/');
window.String(NO_TRANSLATION);
window.String("] "sv);

SelectStyle(window, Style::INPUT);
window.ClearToEol();

echo();
curs_set(1);
}

static constexpr bool
IsCancelKey(int key) noexcept
{
return key == KEY_CANCEL || key == KEY_SCANCEL ||
key == KEY_CLOSE ||
key == KEY_UNDO ||
key == KEY_CTL('C') ||
key == KEY_CTL('G') ||
key == KEY_ESCAPE;
}

bool
YesNoDialog::OnKey(Window, int key)
{
/* NOTE: if one day a translator decides to use a multi-byte character
for one of the yes/no keys, we'll have to parse it properly */

key = tolower(key);
if (key == YES_TRANSLATION[0]) {
Hide();
SetResult(YesNoResult::YES);
} else if (key == NO_TRANSLATION[0]) {
Hide();
SetResult(YesNoResult::NO);
} else if (IsCancelKey(key)) {
Cancel();
} else {
Bell();
}

return true;
}
84 changes: 84 additions & 0 deletions src/dialogs/YesNoDialog.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project

#pragma once

#include "ModalDialog.hxx"
#include "co/AwaitableHelper.hxx"

#include <cstdint>
#include <string_view>

enum class YesNoResult : int8_t {
CANCEL = -1,
NO = false,
YES = true,
};

/**
* A #ModalDialog that asks the user a yes/no question.
*
* This dialog is supposed to be awaited from a coroutine using
* co_await. It suspends the caller while waiting for user input.
*/
class YesNoDialog final : public ModalDialog {
const std::string_view prompt;

std::coroutine_handle<> continuation;

bool ready = false;
YesNoResult result;

using Awaitable = Co::AwaitableHelper<YesNoDialog, false>;
friend Awaitable;

public:
/**
* @param _prompt the human-readable prompt to be displayed
* (including question mark if desired); the pointed-by memory
* is owned by the caller and must remain valid during the
* lifetime of this dialog
*/
YesNoDialog(ModalDock &_dock, std::string_view _prompt) noexcept
:ModalDialog(_dock), prompt(_prompt)
{
Show();
}

~YesNoDialog() noexcept {
Hide();
}

/**
* Await completion of this dialog.
*
* @return a YesNoResult
*/
Awaitable operator co_await() noexcept {
return *this;
}

private:
void SetResult(YesNoResult _result) noexcept {
result = _result;
ready = true;

if (continuation)
continuation.resume();
}

bool IsReady() const noexcept {
return ready;
}

YesNoResult TakeValue() noexcept {
return result;
}

public:
/* virtual methodds from Modal */
void OnLeave(Window window) noexcept override;
void OnCancel() noexcept override;
void Paint(Window window) const noexcept override;
bool OnKey(Window window, int key) override;
};
2 changes: 2 additions & 0 deletions src/dialogs/meson.build
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
dialogs = static_library(
'dialogs',
'ModalDialog.cxx',
'YesNoDialog.cxx',
include_directories: inc,
dependencies: [
ui_dep,
intl_dep,
],
)

Expand Down
12 changes: 10 additions & 2 deletions src/save_playlist.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include "charset.hxx"
#include "mpdclient.hxx"
#include "Completion.hxx"
#include "screen.hxx"
#include "screen_utils.hxx"
#include "dialogs/YesNoDialog.hxx"
#include "co/InvokeTask.hxx"

#include <mpd/client.h>
Expand Down Expand Up @@ -89,8 +91,14 @@ playlist_save(ScreenManager &screen, struct mpdclient &c,
char prompt[256];
snprintf(prompt, sizeof(prompt),
_("Replace %s?"), filename.c_str());
bool replace = screen_get_yesno(screen, prompt, false);
if (!replace)

if (co_await YesNoDialog{screen, prompt} != YesNoResult::YES)
co_return;

/* obtain a new connection pointer after
resuming this coroutine */
connection = c.GetConnection();
if (connection == nullptr)
co_return;

if (!mpd_run_rm(connection, filename_utf8.c_str()) ||
Expand Down
19 changes: 0 additions & 19 deletions src/screen_utils.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,6 @@ screen_getch(ScreenManager &screen, const char *prompt) noexcept
return key;
}

bool
screen_get_yesno(ScreenManager &screen, const char *_prompt, bool def) noexcept
{
/* NOTE: if one day a translator decides to use a multi-byte character
for one of the yes/no keys, we'll have to parse it properly */

char prompt[256];
snprintf(prompt, sizeof(prompt),
"%s [%s/%s] ", _prompt,
YES_TRANSLATION, NO_TRANSLATION);
int key = tolower(screen_getch(screen, prompt));
if (key == YES_TRANSLATION[0])
return true;
else if (key == NO_TRANSLATION[0])
return false;
else
return def;
}

std::string
screen_readln(ScreenManager &screen, const char *prompt,
const char *value,
Expand Down
10 changes: 0 additions & 10 deletions src/screen_utils.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,6 @@ class ScreenManager;
int
screen_getch(ScreenManager &screen, const char *prompt) noexcept;

/**
* display a prompt, wait for the user to press a key, and compare it with
* the default keys for "yes" and "no" (and their upper-case pendants).
*
* @returns true, if the user pressed the key for "yes"; false, if the user
* pressed the key for "no"; def otherwise
*/
bool
screen_get_yesno(ScreenManager &screen, const char *prompt, bool def) noexcept;

std::string
screen_read_password(ScreenManager &screen, const char *prompt) noexcept;

Expand Down

0 comments on commit 199b1cb

Please sign in to comment.