MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
mxvk::VK_Console Class Reference

Lightweight command console renderer for mxvk::VK_Window. More...

#include <mxvk/include/mxvk/mxvk_console.hpp>

Public Types

using CommandCallback = std::function<bool(VK_Window &window, const std::vector<std::string> &args, std::ostream &output)>

Public Member Functions

void attach (VK_Window &window, const std::string &fontPath, int fontSize=18)
 Attach this console to a window and configure console font rendering.
void clear ()
 Clear all buffered output lines.
void draw ()
 Draw the current console overlay.
void handleEvent (const SDL_Event &event)
 Handle keyboard/text events for the console.
void hide () noexcept
 Hide console.
const std::string & inputBuffer () const noexcept
void invalidateLayoutCache ()
 Recompute cached line wrapping after font or layout metrics change.
bool isVisible () const noexcept
void printLine (const std::string &line, SDL_Color color=SDL_Color{255, 255, 255, 255})
 Append one line to console output.
void setCommandCallback (CommandCallback callback)
 Register a command callback.
void setFont (const std::string &fontPath, int fontSize)
 Change only the console font, leaving the window default font unchanged.
void setMaxLines (std::size_t maxLines)
 Set max number of retained output lines.
void setMaxVisibleLines (std::size_t maxVisibleLines)
 Set max number of lines rendered on screen.
void setPrompt (const std::string &prompt)
 Set the input prompt text.
void setSpriteYOriginTopLeft (bool enabled) noexcept
 Select whether console sprite elements use top-left Y coordinates.
void setVisible (bool value) noexcept
 Show or hide the console.
void show () noexcept
 Show console.
void toggle () noexcept
 Toggle console visibility.
 VK_Console ()=default

Detailed Description

Lightweight command console renderer for mxvk::VK_Window.

The console captures keyboard input, keeps command history, renders buffered lines via VK_Window::printText, and allows applications to register a command callback.

Definition at line 32 of file mxvk_console.hpp.

Member Typedef Documentation

◆ CommandCallback

using mxvk::VK_Console::CommandCallback = std::function<bool(VK_Window &window, const std::vector<std::string> &args, std::ostream &output)>

Definition at line 34 of file mxvk_console.hpp.

Constructor & Destructor Documentation

◆ VK_Console()

mxvk::VK_Console::VK_Console ( )
default

Member Function Documentation

◆ attach()

void mxvk::VK_Console::attach ( VK_Window & window,
const std::string & fontPath,
int fontSize = 18 )

Attach this console to a window and configure console font rendering.

Definition at line 18 of file mxvk_console.cpp.

18 {
19 windowPtr = &window;
20 setFont(fontPath, fontSize);
21 panel_sprite = nullptr;
22 cursor_sprite = nullptr;
23 scroll_track_sprite = nullptr;
24 scroll_thumb_sprite = nullptr;
25 visible = false;
26 fade_active = false;
27 fade_alpha = 0.0f;
28 fade_start_alpha = 0.0f;
29 fade_target_alpha = 0.0f;
30 fade_start_ns = 0;
31 history_index = -1;
32 cursor_pos = 0;
33 input.clear();
34 scroll_offset = 0;
35 follow_tail = true;
36 scrollbar_dragging = false;
37 scrollbar_drag_offset = 0;
38 }
void setFont(const std::string &fontPath, int fontSize)
Change only the console font, leaving the window default font unchanged.

◆ clear()

void mxvk::VK_Console::clear ( )

Clear all buffered output lines.

Definition at line 136 of file mxvk_console.cpp.

136 {
137 total_line_chars = 0;
138 lines.clear();
139 invalidateWrappedCache();
140 input.clear();
141 cursor_pos = 0;
142 scroll_offset = 0;
143 follow_tail = true;
144 history_index = -1;
145 }

◆ draw()

void mxvk::VK_Console::draw ( )

Draw the current console overlay.

Call once per frame from proc().

Definition at line 721 of file mxvk_console.cpp.

721 {
722 if (windowPtr == nullptr) {
723 return;
724 }
725
726 if (fade_active) {
727 const Uint64 now = SDL_GetTicksNS();
728 const double elapsed = static_cast<double>(now - fade_start_ns);
729 const double duration = static_cast<double>(fade_duration_ns);
730 const float t = static_cast<float>(std::clamp(elapsed / std::max(1.0, duration), 0.0, 1.0));
731 fade_alpha = fade_start_alpha + ((fade_target_alpha - fade_start_alpha) * t);
732 if (t >= 1.0f) {
733 fade_alpha = fade_target_alpha;
734 fade_active = false;
735 }
736 }
737
738 if (!(visible || fade_alpha > 0.0f)) {
739 return;
740 }
741
742 const float alpha = std::clamp(fade_alpha, 0.0f, 1.0f);
743 if (alpha <= 0.0f) {
744 return;
745 }
746
747 const auto scaledColor = [alpha](const SDL_Color &src) {
748 SDL_Color out = src;
749 const int scaled = static_cast<int>(std::lround(static_cast<double>(src.a) * alpha));
750 out.a = static_cast<Uint8>(std::clamp(scaled, 0, 255));
751 return out;
752 };
753
754 updatePanelLayout();
755 ensurePanelSprite();
756 ensureCursorSprite();
757 ensureScrollSprites();
758 const auto consoleSpriteY = [this](const int y, const int h) {
759 if (sprite_y_origin_top_left) {
760 return std::max(0, y);
761 }
762
763 const int screen_h = static_cast<int>(windowPtr->getSwapchainExtent().height);
764 return std::max(0, screen_h - y - h);
765 };
766
767 if (panel_sprite != nullptr) {
768 SDL_Surface *surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
769 if (surface != nullptr) {
770 const SDL_PixelFormatDetails *const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
771 const int panel_alpha = static_cast<int>(std::lround(170.0 * alpha));
772 SDL_FillSurfaceRect(surface, nullptr, SDL_MapRGBA(fmt, nullptr, 0, 0, 0, static_cast<Uint8>(std::clamp(panel_alpha, 0, 255))));
773 panel_sprite->updateTexture(surface);
774 SDL_DestroySurface(surface);
775 }
776
777 panel_sprite->drawSpriteRect(panel_x, consoleSpriteY(panel_y, panel_h), panel_w, panel_h);
778 }
779
780 int glyph_w = 8;
781 int glyph_h = 18;
782 if (!windowPtr->getTextDimensions("M", glyph_w, glyph_h, console_font)) {
783 glyph_h = 18;
784 }
785
786 const int line_height = std::max(1, glyph_h + 2);
787 const int padding = 10;
788 const int text_width = usableTextWidth();
789 ensureWrappedCache(text_width);
790
791 int y = panel_y + padding;
792 windowPtr->printText("MXVK Console (F3 to toggle)", panel_x + padding, y, scaledColor(info_color), console_font);
793 y += line_height;
794
795 const int input_y = panel_y + panel_h - padding - line_height;
796 const int available_height = std::max(line_height, input_y - y - 6);
797 content_top_y = y;
798 content_bottom_y = y + available_height;
799 last_visible_line_count = std::max<std::size_t>(1, static_cast<std::size_t>(available_height / line_height));
800 if (follow_tail) {
801 scroll_offset = 0;
802 }
803
804 const std::size_t max_offset = maxScrollOffset();
805 scroll_offset = std::min(scroll_offset, max_offset);
806 updateScrollbarGeometry();
807
808 const std::size_t visible_lines = std::min(effectiveVisibleLineCount(), wrapped_cache.size());
809 const std::size_t start = (wrapped_cache.size() > visible_lines) ? (wrapped_cache.size() - visible_lines - scroll_offset) : 0;
810 const std::size_t end = std::min(wrapped_cache.size(), start + visible_lines);
811 for (std::size_t i = start; i < end; ++i) {
812 windowPtr->printText(wrapped_cache[i].text, panel_x + padding, y, scaledColor(wrapped_cache[i].color), console_font);
813 y += line_height;
814 }
815
816 if (scroll_track_sprite != nullptr && scrollbar_h > 0) {
817 SDL_Surface *surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
818 if (surface != nullptr) {
819 const SDL_PixelFormatDetails *const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
820 const int track_alpha = static_cast<int>(std::lround(96.0 * alpha));
821 SDL_FillSurfaceRect(surface, nullptr, SDL_MapRGBA(fmt, nullptr, 255, 255, 255, static_cast<Uint8>(std::clamp(track_alpha, 0, 255))));
822 scroll_track_sprite->updateTexture(surface);
823 SDL_DestroySurface(surface);
824 }
825
826 scroll_track_sprite->drawSpriteRect(scrollbar_x, consoleSpriteY(scrollbar_y, scrollbar_h), scrollbar_w, scrollbar_h);
827 }
828
829 if (scroll_thumb_sprite != nullptr && scrollbar_thumb_h > 0) {
830 SDL_Surface *surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
831 if (surface != nullptr) {
832 const SDL_PixelFormatDetails *const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
833 const int base_alpha = scrollbar_dragging ? 240 : 190;
834 const int thumb_alpha = static_cast<int>(std::lround(static_cast<double>(base_alpha) * alpha));
835 SDL_FillSurfaceRect(surface, nullptr, SDL_MapRGBA(fmt, nullptr, 190, 225, 255, static_cast<Uint8>(std::clamp(thumb_alpha, 0, 255))));
836 scroll_thumb_sprite->updateTexture(surface);
837 SDL_DestroySurface(surface);
838 }
839
840 scroll_thumb_sprite->drawSpriteRect(scrollbar_x, consoleSpriteY(scrollbar_thumb_y, scrollbar_thumb_h), scrollbar_w, scrollbar_thumb_h);
841 }
842
843 if (scroll_offset > 0) {
844 windowPtr->printText(std::format("^ {} line(s) newer below", scroll_offset), panel_x + padding, input_y - line_height, scaledColor(info_color), console_font);
845 }
846
847 const Uint64 now = SDL_GetTicksNS();
848 if (now - last_cursor_toggle_ns > 500000000ULL) {
849 cursor_visible = !cursor_visible;
850 last_cursor_toggle_ns = now;
851 }
852
853 cursor_pos = std::min(cursor_pos, input.size());
854
855 const std::string line = promptText + input;
856 windowPtr->printText(line, panel_x + padding, input_y, scaledColor(prompt_color), console_font);
857
858 if (cursor_visible && cursor_sprite != nullptr) {
859 int prefix_width = 0;
860 int prefix_height = 0;
861 const std::string prefix = promptText + input.substr(0, cursor_pos);
862 if (!windowPtr->getTextDimensions(prefix, prefix_width, prefix_height, console_font)) {
863 prefix_width = static_cast<int>((promptText.size() + cursor_pos) * static_cast<std::size_t>(glyph_w));
864 }
865
866 // Draw a VS Code-like thin insertion caret at the text boundary.
867 const int cursor_w = 2;
868 const int cursor_x = std::max(panel_x + padding, panel_x + padding + prefix_width - 1);
869 const int cursor_y = input_y + 2;
870 const int cursor_h = std::max(4, line_height - 4);
871
872 SDL_Surface *surface = SDL_CreateSurface(2, 2, SDL_PIXELFORMAT_RGBA32);
873 if (surface != nullptr) {
874 const SDL_PixelFormatDetails *const fmt = SDL_GetPixelFormatDetails(SDL_PIXELFORMAT_RGBA32);
875 const int cursor_alpha = static_cast<int>(std::lround(220.0 * alpha));
876 SDL_FillSurfaceRect(surface, nullptr, SDL_MapRGBA(fmt, nullptr, 210, 255, 220, static_cast<Uint8>(std::clamp(cursor_alpha, 0, 255))));
877 cursor_sprite->updateTexture(surface);
878 SDL_DestroySurface(surface);
879 }
880
881 cursor_sprite->drawSpriteRect(cursor_x, consoleSpriteY(cursor_y, cursor_h), cursor_w, cursor_h);
882 }
883 }

◆ handleEvent()

void mxvk::VK_Console::handleEvent ( const SDL_Event & event)

Handle keyboard/text events for the console.

Definition at line 606 of file mxvk_console.cpp.

606 {
607 if (event.type == SDL_EVENT_KEY_DOWN && (event.key.key == SDLK_F3)) {
608 toggle();
609 return;
610 }
611
612 if (!visible) {
613 return;
614 }
615
616 refreshVisibleLineCount();
617
618 if (event.type == SDL_EVENT_MOUSE_WHEEL) {
619 if (event.wheel.y > 0) {
620 scrollUp(static_cast<std::size_t>(event.wheel.y));
621 } else if (event.wheel.y < 0) {
622 scrollDown(static_cast<std::size_t>(-event.wheel.y));
623 }
624 return;
625 }
626
627 if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) {
628 const int mx = event.button.x;
629 const int my = event.button.y;
630 if (isPointInScrollbarThumb(mx, my)) {
631 scrollbar_dragging = true;
632 scrollbar_drag_offset = my - scrollbar_thumb_y;
633 return;
634 }
635
636 if (isPointInScrollbar(mx, my)) {
637 const std::size_t page = std::max<std::size_t>(1, effectiveVisibleLineCount() - 1);
638 if (my < scrollbar_thumb_y) {
639 scrollUp(page);
640 } else if (my >= (scrollbar_thumb_y + scrollbar_thumb_h)) {
641 scrollDown(page);
642 }
643 return;
644 }
645 }
646
647 if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) {
648 scrollbar_dragging = false;
649 return;
650 }
651
652 if (event.type == SDL_EVENT_MOUSE_MOTION && scrollbar_dragging) {
653 updateScrollFromThumbY(event.motion.y - scrollbar_drag_offset);
654 return;
655 }
656
657 if (event.type == SDL_EVENT_KEY_DOWN) {
658 switch (event.key.key) {
659 case SDLK_RETURN:
660 case SDLK_KP_ENTER:
661 submitInput();
662 return;
663 case SDLK_BACKSPACE:
664 if (cursor_pos > 0 && cursor_pos <= input.size()) {
665 input.erase(cursor_pos - 1, 1);
666 --cursor_pos;
667 }
668 return;
669 case SDLK_DELETE:
670 if (cursor_pos < input.size()) {
671 input.erase(cursor_pos, 1);
672 }
673 return;
674 case SDLK_LEFT:
675 if (cursor_pos > 0) {
676 --cursor_pos;
677 }
678 return;
679 case SDLK_RIGHT:
680 if (cursor_pos < input.size()) {
681 ++cursor_pos;
682 }
683 return;
684 case SDLK_HOME:
685 cursor_pos = 0;
686 return;
687 case SDLK_END:
688 follow_tail = true;
689 scroll_offset = 0;
690 cursor_pos = input.size();
691 return;
692 case SDLK_UP:
693 historyUp();
694 return;
695 case SDLK_DOWN:
696 historyDown();
697 return;
698 case SDLK_PAGEUP:
699 scrollUp(std::max<std::size_t>(1, effectiveVisibleLineCount() - 1));
700 return;
701 case SDLK_PAGEDOWN:
702 scrollDown(std::max<std::size_t>(1, effectiveVisibleLineCount() - 1));
703 return;
704 case SDLK_ESCAPE:
705 hide();
706 return;
707 default:
708 break;
709 }
710 return;
711 }
712
713 if (event.type == SDL_EVENT_TEXT_INPUT) {
714 if (event.text.text[0] != '\0') {
715 input.insert(cursor_pos, event.text.text);
716 cursor_pos += SDL_strlen(event.text.text);
717 }
718 }
719 }
void toggle() noexcept
Toggle console visibility.
void hide() noexcept
Hide console.

◆ hide()

void mxvk::VK_Console::hide ( )
noexcept

Hide console.

Definition at line 80 of file mxvk_console.cpp.

80{ setVisible(false); }
void setVisible(bool value) noexcept
Show or hide the console.

◆ inputBuffer()

const std::string & mxvk::VK_Console::inputBuffer ( ) const
nodiscardnoexcept
Returns
Current input buffer.

Definition at line 106 of file mxvk_console.cpp.

106{ return input; }

◆ invalidateLayoutCache()

void mxvk::VK_Console::invalidateLayoutCache ( )

Recompute cached line wrapping after font or layout metrics change.

Definition at line 101 of file mxvk_console.cpp.

101 {
102 invalidateWrappedCache();
103 scroll_offset = std::min(scroll_offset, maxScrollOffset());
104 }

◆ isVisible()

bool mxvk::VK_Console::isVisible ( ) const
nodiscardnoexcept
Returns
true when the console is visible.

Definition at line 90 of file mxvk_console.cpp.

90{ return visible || fade_alpha > 0.0f || fade_active; }

◆ printLine()

void mxvk::VK_Console::printLine ( const std::string & line,
SDL_Color color = SDL_Color{255, 255, 255, 255} )

Append one line to console output.

Parameters
lineText to append. Embedded newlines are split into separate lines.
colorOptional text color for this output line. Defaults to white.

Definition at line 108 of file mxvk_console.cpp.

108 {
109 std::size_t start = 0;
110 while (start <= line.size()) {
111 const std::size_t nl = line.find('\n', start);
112 if (nl == std::string::npos) {
113 pushOutputLine(line.substr(start), color);
114 break;
115 }
116
117 pushOutputLine(line.substr(start, nl - start), color);
118 start = nl + 1;
119
120 // Preserve a trailing newline as an empty visual line.
121 if (start == line.size()) {
122 pushOutputLine(std::string{}, color);
123 break;
124 }
125 }
126
127 trimOutputToLimits();
128 invalidateWrappedCache();
129 if (follow_tail) {
130 scroll_offset = 0;
131 } else {
132 scroll_offset = std::min(scroll_offset, maxScrollOffset());
133 }
134 }

◆ setCommandCallback()

void mxvk::VK_Console::setCommandCallback ( CommandCallback callback)

Register a command callback.

Return true to indicate the command was handled.

Definition at line 46 of file mxvk_console.cpp.

46{ commandCallback = std::move(callback); }

◆ setFont()

void mxvk::VK_Console::setFont ( const std::string & fontPath,
int fontSize )

Change only the console font, leaving the window default font unchanged.

Definition at line 40 of file mxvk_console.cpp.

40 {
41 console_font.reset(fontPath, fontSize);
43 refreshVisibleLineCount();
44 }
void invalidateLayoutCache()
Recompute cached line wrapping after font or layout metrics change.

◆ setMaxLines()

void mxvk::VK_Console::setMaxLines ( std::size_t maxLines)

Set max number of retained output lines.

Definition at line 92 of file mxvk_console.cpp.

92 {
93 max_lines = std::max<std::size_t>(1, maxLines);
94 trimOutputToLimits();
95 invalidateWrappedCache();
96 scroll_offset = std::min(scroll_offset, maxScrollOffset());
97 }

◆ setMaxVisibleLines()

void mxvk::VK_Console::setMaxVisibleLines ( std::size_t maxVisibleLines)

Set max number of lines rendered on screen.

Definition at line 99 of file mxvk_console.cpp.

99{ max_visible_lines = std::max<std::size_t>(1, maxVisibleLines); }

◆ setPrompt()

void mxvk::VK_Console::setPrompt ( const std::string & prompt)

Set the input prompt text.

Definition at line 48 of file mxvk_console.cpp.

48 {
49 if (!prompt.empty()) {
50 promptText = prompt;
51 }
52 }

◆ setSpriteYOriginTopLeft()

void mxvk::VK_Console::setSpriteYOriginTopLeft ( bool enabled)
inlinenoexcept

Select whether console sprite elements use top-left Y coordinates.

Definition at line 91 of file mxvk_console.hpp.

91{ sprite_y_origin_top_left = enabled; }

◆ setVisible()

void mxvk::VK_Console::setVisible ( bool value)
noexcept

Show or hide the console.

Definition at line 54 of file mxvk_console.cpp.

54 {
55 if (value) {
56 visible = true;
57 fade_start_alpha = fade_alpha;
58 fade_target_alpha = 1.0f;
59 fade_start_ns = SDL_GetTicksNS();
60 fade_active = true;
61 if (windowPtr != nullptr) {
62 SDL_StartTextInput(windowPtr->getSDLWindow());
63 }
64 follow_tail = true;
65 scroll_offset = 0;
66 } else {
67 visible = false;
68 fade_start_alpha = fade_alpha;
69 fade_target_alpha = 0.0f;
70 fade_start_ns = SDL_GetTicksNS();
71 fade_active = true;
72 if (windowPtr != nullptr) {
73 SDL_StopTextInput(windowPtr->getSDLWindow());
74 }
75 }
76 }

◆ show()

void mxvk::VK_Console::show ( )
noexcept

Show console.

Definition at line 78 of file mxvk_console.cpp.

78{ setVisible(true); }

◆ toggle()

void mxvk::VK_Console::toggle ( )
noexcept

Toggle console visibility.

Definition at line 82 of file mxvk_console.cpp.

82 {
83 if (visible) {
84 hide();
85 } else {
86 show();
87 }
88 }
void show() noexcept
Show console.

The documentation for this class was generated from the following files: