Skip to content

Commit 65ec903

Browse files
committed
Menu fallbacks for EGA/VGA terminal.
1 parent 2cd5929 commit 65ec903

File tree

6 files changed

+98
-45
lines changed

6 files changed

+98
-45
lines changed

ReleaseNotes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
## Release Notes
22

3+
### NWindows 0.0.2-beta1
4+
5+
- Polyfill checkbox/radio-button/dropdon characters on EGA/VGA (Linux) terminals.
6+
- can_display_character tests display font on Linux EGA/VGA terminals.
7+
- Fallback to ASCII for particularly incapable terminals.
8+
9+
310
### NWindows 0.0.1-beta1
411

512
Initial release of the NWindows Library.

examples/nwindows_test/ascii_fallback_test_window.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,12 @@ class NCharacterTestWindow: public NElement {
7373
return { 2,1 };
7474
}
7575
virtual void render() override {
76-
cchar_t ch;
77-
memset(&ch,0,sizeof(ch));
78-
ch.chars[0] = this->c;
79-
ch.chars[1] = 0;
80-
81-
bool valid = wadd_wch(stdscr,&ch) != ERR;
76+
bool valid = window()->can_display_character(this->c);
8277
move(0,0);
8378
wchar_t str[2] = {this->c,0};
8479
if (!valid) {
8580
str[0] = L'X';
8681
}
87-
if (ch.chars[0] != this->c) {
88-
str[0] = L'?';
89-
}
9082
print(str);
9183
}
9284
private:
@@ -184,7 +176,7 @@ void ascii_fallback_test_window(NWindow::ptr parentWindow /* = nullptr */)
184176
| margin({ 2,1,2,1 })
185177
| row_gap(1)
186178
| add_child(
187-
NTextElement::create("Fallbacks only occur on non-Unicode terminals. ◉●○🗹□☑☐")
179+
NTextElement::create("Fallbacks only occur on non-Unicode terminals. ●○ ☑☐ ⏵⏷ ◉🗹□")
188180
| wrap_text()
189181
)
190182
| add_child(

src/ConsoleFontTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void test_font(
5656
<< " characters: " << font->character_count()
5757
<< " map: " << (font->has_unicode_map() ? "yes" : "no")
5858
<< " extra: " << (font->extra_bytes());
59-
std::u32string sacrificialCharacters = ::nwindows::internal::get_sacrificial_characters(font, 4);
59+
std::u32string sacrificialCharacters = ::nwindows::internal::get_sacrificial_characters(font, 6);
6060
std::cout << " sac: " << u32string_to_utf8(sacrificialCharacters) << std::endl;
6161

6262
std::cout << std::endl;

src/NWindows.cpp

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -790,8 +790,24 @@ void NWindow::close()
790790

791791
if (curses_window_)
792792
{
793+
794+
795+
try {
796+
// eliminate red color flash as palette changes during shutdown.
797+
if (color_palette_->DesktopBackground != color_palette_->Black) {
798+
if (max_colors_ > 8) {
799+
auto defaultColor = make_color_pair(color_palette_->White, color_palette_->Black);
800+
wbkgd(root_curses_window_, defaultColor.attribute());
801+
}
802+
}
803+
}
804+
catch (const std::exception& ignore)
805+
{
806+
(void)ignore; // ran out of colors? ignore.
807+
}
808+
793809
clear_window();
794-
//q restore_colors();
810+
795811
del_panel(curses_panel_);
796812
delwin(curses_window_);
797813

@@ -1320,8 +1336,10 @@ void NWindow::init_root_window()
13201336
init_color(COLOR_WHITE, color_palette_->White);
13211337

13221338
if (color_palette_->DesktopBackground != color_palette_->Black) {
1323-
auto desktopColor = make_color_pair(color_palette_->DesktopBackground, color_palette_->DesktopBackground);
1324-
wbkgd(root_curses_window_, desktopColor.attribute());
1339+
if (max_colors_ > 8) {
1340+
auto desktopColor = make_color_pair(color_palette_->DesktopBackground, color_palette_->DesktopBackground);
1341+
wbkgd(root_curses_window_, desktopColor.attribute());
1342+
}
13251343
}
13261344

13271345

@@ -4630,7 +4648,7 @@ std::string NCheckboxElement::unchecked_text() const
46304648
{
46314649
if (window()->console_font())
46324650
{
4633-
return ""; // u2610
4651+
return ""; // u2610
46344652
}
46354653
if (use_raspberry_pi_fallback() && (!window()->console_font()))
46364654
{
@@ -5742,22 +5760,22 @@ void NPopupMenuWindow::handle_cancelled()
57425760
static const std::string CHECKMARK_PREFIX = "";
57435761
static const std::string ASCII_CHECKMARK_PREFIX = " X ";
57445762

5745-
static std::string checkmark_prefix(bool is_unicode) {
5746-
if (is_unicode)
5763+
static std::string checkmark_prefix(NWindow* window) {
5764+
if (window->can_display_character(L''))
57475765
{
57485766
return CHECKMARK_PREFIX;
57495767
}
57505768
return ASCII_CHECKMARK_PREFIX;
57515769
}
5752-
int NPopupMenuWindow::measure_prefix(const NMenuItem& item, bool is_unicode_locale)
5770+
int NPopupMenuWindow::measure_prefix(std::shared_ptr<NWindow> window, const NMenuItem& menuItem)
57535771
{
5754-
if (item.display_check)
5772+
if (menuItem.display_check)
57555773
{
5756-
return this->measure_text(checkmark_prefix(is_unicode_locale));
5774+
return measure_text(checkmark_prefix(this));
57575775
}
5758-
else if (item.icon.length() != 0)
5776+
else if (menuItem.icon.length() != 0)
57595777
{
5760-
return this->measure_text(item.icon) + 2;
5778+
return measure_text(menuItem.icon) + 2;
57615779
}
57625780
else {
57635781
return 1;
@@ -5782,7 +5800,7 @@ void NPopupMenuWindow::Init(NWindow::ptr parentWindow, const std::vector<NMenuIt
57825800

57835801
int prefixWidth = 0;
57845802
for (auto& item : menu_items) {
5785-
prefixWidth = std::max(prefixWidth, measure_prefix(item, parentWindow->is_unicode_locale()));
5803+
prefixWidth = std::max(prefixWidth, measure_prefix(parentWindow, item));
57865804
}
57875805

57885806
for (auto& item : menu_items)
@@ -5838,7 +5856,7 @@ void NPopupMenuWindow::Init(NWindow::ptr parentWindow, const std::vector<NMenuIt
58385856
if (item.display_check) {
58395857
if (item.checked)
58405858
{
5841-
prefix = checkmark_prefix(parentWindow->is_unicode_locale());
5859+
prefix = checkmark_prefix(parentWindow.get());
58425860
}
58435861
}
58445862
else if (item.icon.length() != 0)
@@ -5947,9 +5965,9 @@ void NSubmenuMenuItemElement::open(bool value)
59475965
if (this_)
59485966
{
59495967
this_->on_cancelled.fire(this_);
5968+
this_->popup_window_ = nullptr; // don't call close()
5969+
this_->open(false);
59505970
}
5951-
this_->popup_window_ = nullptr; // don't call close()
5952-
this_->open(false);
59535971
}
59545972
);
59555973
}
@@ -5969,7 +5987,7 @@ void NSubmenuMenuItemElement::open(bool value)
59695987
void NSubmenuMenuItemElement::handle_attached(NWindow* window)
59705988
{
59715989
super::handle_attached(window);
5972-
if (window->is_unicode_locale())
5990+
if (window->can_display_character(L''))
59735991
{
59745992
this->suffix("");
59755993
}
@@ -6200,23 +6218,24 @@ NMessageWindow::ptr NMessageWindow::create(
62006218

62016219
void NMessageWindow::init(NWindow::ptr parent_window, NMessageType message_type, const std::string& message, int text_block_width)
62026220
{
6203-
if (parent_window->is_unicode_locale())
6221+
std::string iconText;
6222+
6223+
switch (message_type)
62046224
{
6205-
// with an icon.
6206-
std::string iconText;
6225+
case NMessageType::Error:
6226+
iconText = "🛑︎\uFE0E"; // (\uFe0E = display b&w variant)
6227+
break;
6228+
case NMessageType::Warning:
6229+
iconText = "⚠️";
6230+
break;
6231+
case NMessageType::Info:
6232+
iconText = "🛈";
6233+
break;
6234+
}
62076235

6208-
switch (message_type)
6209-
{
6210-
case NMessageType::Error:
6211-
iconText = "🛑︎\uFE0E"; // (\uFe0E = display b&w variant)
6212-
break;
6213-
case NMessageType::Warning:
6214-
iconText = "⚠️";
6215-
break;
6216-
case NMessageType::Info:
6217-
iconText = "🛈";
6218-
break;
6219-
}
6236+
if (parent_window->can_display_character(utf8_char32_at(iconText,0)))
6237+
{
6238+
// with an icon.
62206239

62216240
this->add_child(
62226241
NVerticalStackElement::create()

src/SyntheticCharacters.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,36 @@ static std::vector<uint8_t> make_dropdown(
466466
}
467467
return bitmap.data();
468468
}
469+
static std::vector<uint8_t> make_flyout(
470+
const std::vector<uint8_t>& copyrData,
471+
size_t char_width,
472+
size_t char_height)
473+
{
474+
MonoBitmap bitmap(copyrData, char_width, char_height);
475+
CopyrightDimensions info = get_copyright_dimensions(bitmap);
476+
477+
bitmap.clear();
478+
479+
int cx = (info.outer.left()*2 + info.outer.right())/3;
480+
int cy = (info.outer.top() + info.outer.bottom())/2;
481+
for (int ix = 0; ix < (int)char_width; ++ix)
482+
{
483+
if (ix >= cx)
484+
{
485+
int x0 = ix-cx;
486+
x0 = info.outer.width/2-x0;
487+
int top0 = std::max(0,cy-x0);
488+
int top1 = std::min((int)char_height,cy + x0);
489+
490+
for (int iy = top0; iy < top1; ++iy)
491+
{
492+
bitmap.set(ix,iy,true);
493+
}
494+
}
495+
}
496+
return bitmap.data();
497+
}
498+
469499
static std::vector<uint8_t> make_radio_button_unchecked(
470500
const std::vector<uint8_t>& copyrData,
471501
size_t char_width,
@@ -528,9 +558,10 @@ void ::nwindows::internal::add_synthetic_characters(ConsoleFont::ptr font)
528558
char32_t CHECKBOX_CHECKED_CHAR = L'\u2611'; //
529559
char32_t CHECKBOX_UNCHECKED_CHAR = L'\u2610'; //
530560
char32_t DROPDOWN_CHAR = L''; //
561+
char32_t FLYOUT_CHAR = L''; //
531562

532563

533-
auto sacrificial_characters = get_sacrificial_characters(font, 5);
564+
auto sacrificial_characters = get_sacrificial_characters(font, 6);
534565

535566
// use copyright symbol as a template for constructing the new characters.
536567
size_t copyrIndex = font->get_glyph_position(U'\u00A9').value();
@@ -541,12 +572,14 @@ void ::nwindows::internal::add_synthetic_characters(ConsoleFont::ptr font)
541572
auto checkboxChecked = make_checkbox_checked(copyrData, font->char_width(), font->char_height());
542573
auto checkboxUnchecked = make_checkbox_unchecked(copyrData, font->char_width(), font->char_height());
543574
auto dropdown = make_dropdown(copyrData, font->char_width(), font->char_height());
575+
auto flyout = make_flyout(copyrData, font->char_width(), font->char_height());
544576

545577
replace_character(font, sacrificial_characters[0], RADIO_BUTTON_CHECKED_CHAR, radioButtonChecked);
546578
replace_character(font, sacrificial_characters[1], RADIO_BUTTON_UNCHECKED_CHAR, radioButtonUnchecked);
547579
replace_character(font, sacrificial_characters[2], CHECKBOX_CHECKED_CHAR, checkboxChecked);
548580
replace_character(font, sacrificial_characters[3], CHECKBOX_UNCHECKED_CHAR, checkboxUnchecked);
549581
replace_character(font, sacrificial_characters[4], DROPDOWN_CHAR, dropdown);
582+
replace_character(font, sacrificial_characters[5], FLYOUT_CHAR, flyout);
550583
}
551584

552585

src/include/NWindows/NWindows.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,7 +1424,6 @@ namespace nwindows
14241424

14251425
// same locale string as std::setlocale
14261426
static void set_locale(const std::string& locale);
1427-
bool is_unicode_locale() const { return is_unicode_locale_; }
14281427
bool can_display_character(char32_t c) const;
14291428

14301429
std::shared_ptr<ConsoleFont> console_font() const { return this->top_level_window()->console_font_; }
@@ -1616,6 +1615,9 @@ namespace nwindows
16161615
virtual void render() override;
16171616

16181617
private:
1618+
1619+
bool is_unicode_locale() const { return is_unicode_locale_; }
1620+
16191621
std::shared_ptr<internal::ConsoleFontManager> console_font_manager_;
16201622

16211623
std::string fatal_error_message_;
@@ -1946,7 +1948,7 @@ namespace nwindows
19461948
virtual void handle_cancelled();
19471949

19481950
private:
1949-
int measure_prefix(const NMenuItem& menuItem, bool isUnicodeLocale);
1951+
int measure_prefix(std::shared_ptr<NWindow> window, const NMenuItem& menuItem);
19501952
bool was_item_selected_ = false;
19511953

19521954
};

0 commit comments

Comments
 (0)