MXVK Vulkan Framework 0.24.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

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

Initial value:
std::function<bool(VK_Window &window, const std::vector<std::string> &args, std::ostream &output)>
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:37

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 148 of file mxvk_console.cpp.

148 {
149 total_line_chars = 0;
150 lines.clear();
151 invalidateWrappedCache();
152 input.clear();
153 cursor_pos = 0;
154 scroll_offset = 0;
155 follow_tail = true;
156 history_index = -1;
157 }

◆ draw()

void mxvk::VK_Console::draw ( )

Draw the current console overlay.

Call once per frame from proc().

Definition at line 741 of file mxvk_console.cpp.

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

◆ handleEvent()

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

Handle keyboard/text events for the console.

Definition at line 626 of file mxvk_console.cpp.

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

◆ hide()

void mxvk::VK_Console::hide ( )
noexcept

Hide console.

Definition at line 84 of file mxvk_console.cpp.

84 {
85 setVisible(false);
86 }
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 116 of file mxvk_console.cpp.

116 {
117 return input;
118 }

◆ invalidateLayoutCache()

void mxvk::VK_Console::invalidateLayoutCache ( )

Recompute cached line wrapping after font or layout metrics change.

Definition at line 111 of file mxvk_console.cpp.

111 {
112 invalidateWrappedCache();
113 scroll_offset = std::min(scroll_offset, maxScrollOffset());
114 }

◆ isVisible()

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

Definition at line 96 of file mxvk_console.cpp.

96 {
97 return visible || fade_alpha > 0.0f || fade_active;
98 }

◆ 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 120 of file mxvk_console.cpp.

120 {
121 std::size_t start = 0;
122 while (start <= line.size()) {
123 const std::size_t nl = line.find('\n', start);
124 if (nl == std::string::npos) {
125 pushOutputLine(line.substr(start), color);
126 break;
127 }
128
129 pushOutputLine(line.substr(start, nl - start), color);
130 start = nl + 1;
131
132 // Preserve a trailing newline as an empty visual line.
133 if (start == line.size()) {
134 pushOutputLine(std::string{}, color);
135 break;
136 }
137 }
138
139 trimOutputToLimits();
140 invalidateWrappedCache();
141 if (follow_tail) {
142 scroll_offset = 0;
143 } else {
144 scroll_offset = std::min(scroll_offset, maxScrollOffset());
145 }
146 }

◆ 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 {
47 commandCallback = std::move(callback);
48 }

◆ 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 100 of file mxvk_console.cpp.

100 {
101 max_lines = std::max<std::size_t>(1, maxLines);
102 trimOutputToLimits();
103 invalidateWrappedCache();
104 scroll_offset = std::min(scroll_offset, maxScrollOffset());
105 }

◆ setMaxVisibleLines()

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

Set max number of lines rendered on screen.

Definition at line 107 of file mxvk_console.cpp.

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

◆ setPrompt()

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

Set the input prompt text.

Definition at line 50 of file mxvk_console.cpp.

50 {
51 if (!prompt.empty()) {
52 promptText = prompt;
53 }
54 }

◆ setSpriteYOriginTopLeft()

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

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

Definition at line 92 of file mxvk_console.hpp.

92{ sprite_y_origin_top_left = enabled; }

◆ setVisible()

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

Show or hide the console.

Definition at line 56 of file mxvk_console.cpp.

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

◆ show()

void mxvk::VK_Console::show ( )
noexcept

Show console.

Definition at line 80 of file mxvk_console.cpp.

80 {
81 setVisible(true);
82 }

◆ toggle()

void mxvk::VK_Console::toggle ( )
noexcept

Toggle console visibility.

Definition at line 88 of file mxvk_console.cpp.

88 {
89 if (visible) {
90 hide();
91 } else {
92 show();
93 }
94 }
void show() noexcept
Show console.

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