Compare commits
8 Commits
aa1ab8f533
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1000736389 | ||
|
|
e999b2ff5c | ||
|
|
c658bb6cd7 | ||
|
|
a69d47b879 | ||
|
|
e749618afd | ||
|
|
59f2880498 | ||
|
|
7531eacf64 | ||
|
|
8825bc38d7 |
18
.clang-format
Normal file
18
.clang-format
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
ColumnLimit: 120
|
||||
BreakBeforeBraces: Attach
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
IndentCaseLabels: true
|
||||
SortIncludes: Never
|
||||
ReflowComments: false
|
||||
PointerAlignment: Left
|
||||
ReferenceAlignment: Left
|
||||
SpaceBeforeParens: ControlStatements
|
||||
...
|
||||
9
LICENSE
Normal file
9
LICENSE
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html><head>
|
||||
<title>301 Moved Permanently</title>
|
||||
</head><body>
|
||||
<h1>Moved Permanently</h1>
|
||||
<p>The document has moved <a href="https://www.wtfpl.net/txt/copying/">here</a>.</p>
|
||||
<hr>
|
||||
<address>Apache/2.4.66 (Debian) Server at www.wtfpl.net Port 80</address>
|
||||
</body></html>
|
||||
23
README.md
23
README.md
@@ -9,6 +9,7 @@ Reusable NVGT helpers for Storm projects.
|
||||
- `text_reader_compat.nvgt`: Optional aliases (`text_reader*`) for legacy code.
|
||||
- `ui.nvgt`: Dialog wrappers with optional main-window restoration.
|
||||
- `speech_history.nvgt`: `speak_with_history()` wrapper plus comma/period history navigation.
|
||||
- `character_dialog.nvgt`: Blocking dialog lines with repeat/next/skip keys and optional per-word pre-speech sounds.
|
||||
- `notifications.nvgt`: Queued notifications with history and optional sound playback.
|
||||
- `menu_helpers.nvgt`: Simple menu runner + filter helpers with `sounds/menu` defaults.
|
||||
- `menu_music.nvgt`: Reusable menu music start/pause/stop helpers with blocking fade-out pause by default.
|
||||
@@ -29,6 +30,7 @@ Reusable NVGT helpers for Storm projects.
|
||||
#include "libstorm-nvgt/text_reader_compat.nvgt" // Optional legacy aliases
|
||||
#include "libstorm-nvgt/ui.nvgt"
|
||||
#include "libstorm-nvgt/speech_history.nvgt"
|
||||
#include "libstorm-nvgt/character_dialog.nvgt"
|
||||
#include "libstorm-nvgt/notifications.nvgt"
|
||||
#include "libstorm-nvgt/menu_helpers.nvgt"
|
||||
#include "libstorm-nvgt/menu_music.nvgt"
|
||||
@@ -110,6 +112,22 @@ void stop_main_menu_music() {
|
||||
file_viewer_file("README.txt", "Readme", true);
|
||||
```
|
||||
|
||||
## character_dialog Example
|
||||
|
||||
```nvgt
|
||||
#include "libstorm-nvgt/character_dialog.nvgt"
|
||||
|
||||
void play_intro_dialog() {
|
||||
string[] lines = {"Welcome to the gate.", "State your name and purpose."};
|
||||
character_dialog_show_lines(lines);
|
||||
}
|
||||
```
|
||||
|
||||
```nvgt
|
||||
// Optional override sound. Custom paths play once per line instead of once per word.
|
||||
character_dialog_set_sound_path("sounds/cutscene/chime");
|
||||
```
|
||||
|
||||
## speech_history + notifications Example
|
||||
|
||||
```nvgt
|
||||
@@ -140,7 +158,8 @@ void on_event() {
|
||||
## learn_sounds Example (Project Override File)
|
||||
|
||||
```nvgt
|
||||
// In your project root, create excluded_sounds.nvgt:
|
||||
// The override file can be named and placed anywhere in your project.
|
||||
// Example file: src/sound_settings.nvgt
|
||||
void configure_project_learn_sounds() {
|
||||
learn_sounds_reset_configuration();
|
||||
learn_sounds_add_skip_entry("sounds/menu/");
|
||||
@@ -151,7 +170,7 @@ void configure_project_learn_sounds() {
|
||||
```nvgt
|
||||
// In your game module:
|
||||
#include "libstorm-nvgt/learn_sounds.nvgt"
|
||||
#include "excluded_sounds.nvgt"
|
||||
#include "src/sound_settings.nvgt"
|
||||
#include "libstorm-nvgt/speech_history.nvgt"
|
||||
|
||||
void learn_sounds_bridge_speak(const string &in message, bool interrupt) {
|
||||
|
||||
201
character_dialog.nvgt
Normal file
201
character_dialog.nvgt
Normal file
@@ -0,0 +1,201 @@
|
||||
funcdef void character_dialog_speak_callback(const string& in message, bool interrupt);
|
||||
|
||||
string characterDialogSoundPath = "sounds/dialog";
|
||||
bool characterDialogShowUsageInstructions = true;
|
||||
bool characterDialogUsageInstructionsShown = false;
|
||||
bool characterDialogSoundLoaded = false;
|
||||
sound characterDialogSound;
|
||||
character_dialog_speak_callback @characterDialogSpeakCallback = null;
|
||||
|
||||
void character_dialog_set_speak_callback(character_dialog_speak_callback @callback) {
|
||||
@characterDialogSpeakCallback = @callback;
|
||||
}
|
||||
|
||||
void character_dialog_set_sound_path(const string soundPath) {
|
||||
characterDialogSoundPath = soundPath;
|
||||
characterDialogSoundLoaded = false;
|
||||
characterDialogSound.close();
|
||||
}
|
||||
|
||||
void character_dialog_set_show_usage_instructions(bool enabled) {
|
||||
characterDialogShowUsageInstructions = enabled;
|
||||
}
|
||||
|
||||
void character_dialog_reset_usage_instructions() {
|
||||
characterDialogUsageInstructionsShown = false;
|
||||
}
|
||||
|
||||
void character_dialog_reset() {
|
||||
characterDialogUsageInstructionsShown = false;
|
||||
characterDialogSoundLoaded = false;
|
||||
characterDialogSound.close();
|
||||
}
|
||||
|
||||
void character_dialog_speak(const string& in message, bool interrupt) {
|
||||
if (@characterDialogSpeakCallback !is null) {
|
||||
characterDialogSpeakCallback(message, interrupt);
|
||||
return;
|
||||
}
|
||||
|
||||
screen_reader_speak(message, interrupt);
|
||||
}
|
||||
|
||||
bool character_dialog_should_skip() {
|
||||
return key_pressed(KEY_ESCAPE) || key_pressed(KEY_AC_BACK);
|
||||
}
|
||||
|
||||
bool character_dialog_ensure_sound_loaded() {
|
||||
if (characterDialogSoundLoaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
characterDialogSound.close();
|
||||
if (characterDialogSound.load(characterDialogSoundPath)) {
|
||||
characterDialogSoundLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (characterDialogSound.load(characterDialogSoundPath + ".ogg")) {
|
||||
characterDialogSoundLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!characterDialogSound.load(characterDialogSoundPath + ".wav")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
characterDialogSoundLoaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool character_dialog_use_word_sound_sequence() {
|
||||
if (characterDialogSoundPath == "sounds/dialog" || characterDialogSoundPath == "sounds/dialog.ogg" ||
|
||||
characterDialogSoundPath == "sounds/dialog.wav") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int character_dialog_count_words(const string& in message) {
|
||||
if (message == "") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wordCount = 0;
|
||||
bool inWord = false;
|
||||
for (uint charIndex = 0; charIndex < message.length(); charIndex++) {
|
||||
string character = message.substr(charIndex, 1);
|
||||
bool isWhitespace = character == " " || character == "\t" || character == "\n" || character == "\r";
|
||||
if (isWhitespace) {
|
||||
inWord = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inWord) {
|
||||
wordCount++;
|
||||
inWord = true;
|
||||
}
|
||||
}
|
||||
|
||||
return wordCount;
|
||||
}
|
||||
|
||||
bool character_dialog_play_word_sound_sequence(const string& in message) {
|
||||
if (!character_dialog_ensure_sound_loaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int playCount = 1;
|
||||
if (character_dialog_use_word_sound_sequence()) {
|
||||
playCount = character_dialog_count_words(message);
|
||||
}
|
||||
|
||||
for (int wordIndex = 0; wordIndex < playCount; wordIndex++) {
|
||||
characterDialogSound.play();
|
||||
while (characterDialogSound.playing) {
|
||||
wait(5);
|
||||
if (character_dialog_should_skip()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool character_dialog_repeat_requested() {
|
||||
key_code[] @pressedKeys = keys_pressed();
|
||||
if (@pressedKeys is null || pressedKeys.length() == 0)
|
||||
return false;
|
||||
|
||||
for (uint keyIndex = 0; keyIndex < pressedKeys.length(); keyIndex++) {
|
||||
int keyCode = pressedKeys[keyIndex];
|
||||
if (keyCode == KEY_RETURN || keyCode == KEY_NUMPAD_ENTER || keyCode == KEY_ESCAPE || keyCode == KEY_AC_BACK)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int character_dialog_wait_for_action(const string& in currentLine, bool interrupt) {
|
||||
while (true) {
|
||||
wait(5);
|
||||
|
||||
if (character_dialog_should_skip()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_RETURN) || key_pressed(KEY_NUMPAD_ENTER)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool repeatRequested = character_dialog_repeat_requested();
|
||||
if (repeatRequested) {
|
||||
character_dialog_speak(currentLine, interrupt);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool character_dialog_show_lines(const string[] @dialogLines, bool interrupt = true) {
|
||||
if (@dialogLines is null || dialogLines.length() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (uint lineIndex = 0; lineIndex < dialogLines.length(); lineIndex++) {
|
||||
string currentLine = dialogLines[lineIndex];
|
||||
if (currentLine == "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character_dialog_play_word_sound_sequence(currentLine)) {
|
||||
character_dialog_speak(" ", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
string displayLine = currentLine;
|
||||
if (characterDialogShowUsageInstructions && !characterDialogUsageInstructionsShown) {
|
||||
displayLine += " Press enter to continue, escape to skip, or any other key to repeat.";
|
||||
characterDialogUsageInstructionsShown = true;
|
||||
}
|
||||
|
||||
character_dialog_speak(displayLine, interrupt);
|
||||
int action = character_dialog_wait_for_action(currentLine, interrupt);
|
||||
if (action < 0) {
|
||||
character_dialog_speak(" ", true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
character_dialog_speak(" ", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void character_dialog_show(const string[] @dialogLines, bool interrupt = true) {
|
||||
/* The orange goblins speak to me in the night,
|
||||
as the moon casts shadows pumpkins come to life! */
|
||||
character_dialog_show_lines(dialogLines, interrupt);
|
||||
}
|
||||
@@ -319,7 +319,7 @@ void learn_sounds_collect_entries(string[] @labels, string[] @soundPaths) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string label = learn_sounds_label_from_path(soundPath);
|
||||
string label = i18n_text(learn_sounds_label_from_path(soundPath));
|
||||
string description = learn_sounds_get_description(soundPath);
|
||||
if (description.length() > 0) {
|
||||
label += " - " + description;
|
||||
|
||||
@@ -45,7 +45,9 @@ bool is_windows_reserved_filename(const string& in upperName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sanitize_filename_base(string name, const int maxLength = 40, const string fallback = "item") {
|
||||
string sanitize_filename_base_with_reserved_prefix(string name, const int maxLength = 40,
|
||||
const string fallback = "item",
|
||||
const string reservedPrefix = "file_") {
|
||||
string normalized = normalize_name_whitespace(name);
|
||||
string result = "";
|
||||
bool lastSeparator = false;
|
||||
@@ -93,13 +95,26 @@ string sanitize_filename_base(string name, const int maxLength = 40, const strin
|
||||
}
|
||||
|
||||
if (is_windows_reserved_filename(upperName)) {
|
||||
result = "file_" + result;
|
||||
string safePrefix = reservedPrefix;
|
||||
if (safePrefix == "")
|
||||
safePrefix = "file_";
|
||||
result = safePrefix + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string sanitize_filename_base(string name, const int maxLength = 40, const string fallback = "item") {
|
||||
return sanitize_filename_base_with_reserved_prefix(name, maxLength, fallback, "file_");
|
||||
}
|
||||
|
||||
string build_filename_from_name_ex(const string& in name, const string& in extension = ".dat",
|
||||
const int maxBaseLength = 40, const string fallback = "item",
|
||||
const string reservedPrefix = "file_") {
|
||||
return sanitize_filename_base_with_reserved_prefix(name, maxBaseLength, fallback, reservedPrefix) + extension;
|
||||
}
|
||||
|
||||
string build_filename_from_name(const string& in name, const string& in extension = ".dat",
|
||||
const int maxBaseLength = 40, const string fallback = "item") {
|
||||
return sanitize_filename_base(name, maxBaseLength, fallback) + extension;
|
||||
return build_filename_from_name_ex(name, extension, maxBaseLength, fallback, "file_");
|
||||
}
|
||||
|
||||
76
scripts/format-nvgt.sh
Executable file
76
scripts/format-nvgt.sh
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Show simple usage help.
|
||||
print_usage() {
|
||||
echo "Usage: scripts/format-nvgt.sh [path/to/file.nvgt ...]"
|
||||
echo "Formats all tracked .nvgt files when no file paths are provided."
|
||||
}
|
||||
|
||||
scriptDir=""
|
||||
repoRoot=""
|
||||
filePath=""
|
||||
formattedCount=0
|
||||
targetFiles=()
|
||||
|
||||
# Help flag is optional and exits early without formatting.
|
||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||
print_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Stop immediately if clang-format is not installed.
|
||||
if ! command -v clang-format >/dev/null 2>&1; then
|
||||
echo "clang-format is required but was not found in PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Resolve script location, then run from repo root so relative paths work.
|
||||
scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repoRoot="$(cd "${scriptDir}/.." && pwd)"
|
||||
cd "${repoRoot}"
|
||||
|
||||
# We require the project style file to keep formatting consistent.
|
||||
if [[ ! -f ".clang-format" ]]; then
|
||||
echo "Missing .clang-format in repo root: ${repoRoot}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# No args: format every tracked .nvgt file in git.
|
||||
if [[ "$#" -eq 0 ]]; then
|
||||
mapfile -t targetFiles < <(git ls-files "*.nvgt")
|
||||
else
|
||||
# Args provided: validate each file and format only those paths.
|
||||
for filePath in "$@"; do
|
||||
if [[ ! -f "${filePath}" ]]; then
|
||||
echo "File not found: ${filePath}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${filePath}" != *.nvgt ]]; then
|
||||
echo "Only .nvgt files are supported: ${filePath}" >&2
|
||||
exit 1
|
||||
fi
|
||||
targetFiles+=("${filePath}")
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "${#targetFiles[@]}" -eq 0 ]]; then
|
||||
echo "No .nvgt files found to format."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Force C++ parsing rules for NVGT while still using repo .clang-format.
|
||||
for filePath in "${targetFiles[@]}"; do
|
||||
clang-format -i --style=file --assume-filename=file.cpp "${filePath}"
|
||||
formattedCount=$((formattedCount + 1))
|
||||
done
|
||||
|
||||
echo -n "Formatted ${formattedCount} "
|
||||
if [[ ${formattedCount} -ne 1 ]]; then
|
||||
echo "files."
|
||||
else
|
||||
echo "file."
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -5,6 +5,18 @@ string[] speechHistory;
|
||||
int speechHistoryMaxEntries = 10;
|
||||
int speechHistoryCurrentIndex = -1;
|
||||
bool speechHistoryDeduplicate = true;
|
||||
funcdef string speech_history_message_transform_callback(const string& in message);
|
||||
speech_history_message_transform_callback @speechHistoryMessageTransform = null;
|
||||
|
||||
void speech_history_set_message_transform_callback(speech_history_message_transform_callback @callback) {
|
||||
@speechHistoryMessageTransform = @callback;
|
||||
}
|
||||
|
||||
string speech_history_transform_message(const string& in message) {
|
||||
if (@speechHistoryMessageTransform is null)
|
||||
return message;
|
||||
return speechHistoryMessageTransform(message);
|
||||
}
|
||||
|
||||
void speech_history_set_max_entries(int maxEntries) {
|
||||
if (maxEntries < 1) {
|
||||
@@ -55,27 +67,30 @@ void speech_history_add(const string& in message) {
|
||||
}
|
||||
|
||||
void speak_with_history(const string& in message, bool interrupt) {
|
||||
speech_history_add(message);
|
||||
screen_reader_speak(message, interrupt);
|
||||
string transformedMessage = speech_history_transform_message(message);
|
||||
speech_history_add(transformedMessage);
|
||||
screen_reader_speak(transformedMessage, interrupt);
|
||||
}
|
||||
|
||||
void check_speech_history_keys() {
|
||||
// Comma: older.
|
||||
if (key_pressed(KEY_COMMA)) {
|
||||
if (speechHistory.length() == 0) {
|
||||
screen_reader_speak("No speech history.", true);
|
||||
screen_reader_speak(speech_history_transform_message("No speech history."), true);
|
||||
return;
|
||||
}
|
||||
|
||||
speechHistoryCurrentIndex--;
|
||||
if (speechHistoryCurrentIndex < 0) {
|
||||
speechHistoryCurrentIndex = 0;
|
||||
screen_reader_speak("Oldest message. " + speechHistory[speechHistoryCurrentIndex], true);
|
||||
screen_reader_speak(
|
||||
speech_history_transform_message("Oldest message. " + speechHistory[speechHistoryCurrentIndex]), true);
|
||||
return;
|
||||
}
|
||||
|
||||
int position = speechHistoryCurrentIndex + 1;
|
||||
screen_reader_speak(speechHistory[speechHistoryCurrentIndex] + " " + position + " of " + speechHistory.length(),
|
||||
screen_reader_speak(speech_history_transform_message(speechHistory[speechHistoryCurrentIndex] + " " + position +
|
||||
" of " + speechHistory.length()),
|
||||
true);
|
||||
return;
|
||||
}
|
||||
@@ -83,19 +98,21 @@ void check_speech_history_keys() {
|
||||
// Period: newer.
|
||||
if (key_pressed(KEY_PERIOD)) {
|
||||
if (speechHistory.length() == 0) {
|
||||
screen_reader_speak("No speech history.", true);
|
||||
screen_reader_speak(speech_history_transform_message("No speech history."), true);
|
||||
return;
|
||||
}
|
||||
|
||||
speechHistoryCurrentIndex++;
|
||||
if (speechHistoryCurrentIndex >= int(speechHistory.length())) {
|
||||
speechHistoryCurrentIndex = speechHistory.length() - 1;
|
||||
screen_reader_speak("Newest message. " + speechHistory[speechHistoryCurrentIndex], true);
|
||||
screen_reader_speak(
|
||||
speech_history_transform_message("Newest message. " + speechHistory[speechHistoryCurrentIndex]), true);
|
||||
return;
|
||||
}
|
||||
|
||||
int position = speechHistoryCurrentIndex + 1;
|
||||
screen_reader_speak(speechHistory[speechHistoryCurrentIndex] + " " + position + " of " + speechHistory.length(),
|
||||
screen_reader_speak(speech_history_transform_message(speechHistory[speechHistoryCurrentIndex] + " " + position +
|
||||
" of " + speechHistory.length()),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
26
ui.nvgt
26
ui.nvgt
@@ -2,6 +2,18 @@
|
||||
|
||||
string uiDefaultWindowTitle = "";
|
||||
bool uiUsePromptAsDialogTitle = true;
|
||||
funcdef string ui_text_transform_callback(const string& in text);
|
||||
ui_text_transform_callback @uiTextTransformCallback = null;
|
||||
|
||||
void ui_set_text_transform_callback(ui_text_transform_callback @callback) {
|
||||
@uiTextTransformCallback = @callback;
|
||||
}
|
||||
|
||||
string ui_transform_text(const string& in text) {
|
||||
if (@uiTextTransformCallback is null)
|
||||
return text;
|
||||
return uiTextTransformCallback(text);
|
||||
}
|
||||
|
||||
void ui_set_default_window_title(const string windowTitle) {
|
||||
uiDefaultWindowTitle = windowTitle;
|
||||
@@ -36,20 +48,24 @@ void ui_restore_window(const string windowTitle = "") {
|
||||
|
||||
string ui_input_box(const string title, const string prompt, const string defaultValue = "",
|
||||
const string windowTitle = "") {
|
||||
string dialogTitle = ui_resolve_dialog_title(title, prompt);
|
||||
string result = virtual_input_box(dialogTitle, prompt, defaultValue);
|
||||
string transformedTitle = ui_transform_text(title);
|
||||
string transformedPrompt = ui_transform_text(prompt);
|
||||
string dialogTitle = ui_resolve_dialog_title(transformedTitle, transformedPrompt);
|
||||
string result = virtual_input_box(dialogTitle, transformedPrompt, defaultValue);
|
||||
ui_restore_window(windowTitle);
|
||||
return result;
|
||||
}
|
||||
|
||||
int ui_question(const string title, const string prompt, const string windowTitle = "", const bool canCancel = false) {
|
||||
string dialogTitle = ui_resolve_dialog_title(title, prompt);
|
||||
int result = virtual_question(dialogTitle, prompt, canCancel);
|
||||
string transformedTitle = ui_transform_text(title);
|
||||
string transformedPrompt = ui_transform_text(prompt);
|
||||
string dialogTitle = ui_resolve_dialog_title(transformedTitle, transformedPrompt);
|
||||
int result = virtual_question(dialogTitle, transformedPrompt, canCancel);
|
||||
ui_restore_window(windowTitle);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ui_info_box(const string title, const string heading, const string message, const string windowTitle = "") {
|
||||
virtual_info_box(title, heading, message);
|
||||
virtual_info_box(ui_transform_text(title), ui_transform_text(heading), ui_transform_text(message));
|
||||
ui_restore_window(windowTitle);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user