2020#include " artifacts/ProgramData.hpp"
2121#include " artifacts/SavedGamesSettings.hpp"
2222#include " config.hpp"
23+ #include " licenses.hpp"
2324
2425using namespace FredEmmott ::GUI;
2526using namespace FredEmmott ::GUI::Immediate;
@@ -390,21 +391,94 @@ void ShowArtifacts() {
390391 }
391392}
392393
394+ void ShowLicenses () {
395+ enum class Products { Self, CompressedEmbed, WIL, FUI, Yoga };
396+ constexpr std::array ProductLabels {
397+ std::tuple {Products::Self, " OpenKneeboard Fresh Start" },
398+ std::tuple {Products::CompressedEmbed, " compressed-embed" },
399+ std::tuple {Products::FUI, " FredEmmott::GUI" },
400+ std::tuple {Products::WIL, " Windows Implementation Library" },
401+ std::tuple {Products::Yoga, " Yoga" },
402+ };
403+ static const Licenses licenses {};
404+ static auto key {Products::Self};
405+
406+ const auto layout
407+ = BeginVStackPanel ().Styled (Style ().FlexGrow (1 ).Gap (12 )).Scoped ();
408+
409+ {
410+ const auto card = BeginCard ().Scoped ();
411+ TextBlock (
412+ " Copyright © 2025-present Frederick Emmott\n "
413+ " All rights reserved.\n "
414+ " \n "
415+ " This product contains third-party software components which are "
416+ " licensed separately.\n "
417+ " \n "
418+ " Select a component below to view copyright and license information." )
419+ .Body ();
420+ }
421+
422+ ComboBox (&key, ProductLabels)
423+ .Styled (Style ().AlignSelf (YGAlignStretch))
424+ .Caption (" Component" );
425+
426+ std::string_view license;
427+ switch (key) {
428+ case Products::Self:
429+ license = licenses.SelfAsStringView ();
430+ break ;
431+ case Products::CompressedEmbed:
432+ license = licenses.CompressedEmbedAsStringView ();
433+ break ;
434+ case Products::WIL:
435+ license = licenses.WILAsStringView ();
436+ break ;
437+ case Products::FUI:
438+ license = licenses.FUIAsStringView ();
439+ break ;
440+ case Products::Yoga:
441+ license = licenses.YogaAsStringView ();
442+ break ;
443+ }
444+
445+ const auto card = BeginCard ().Scoped ();
446+ const auto scroll
447+ = BeginVScrollView ().Scoped ().Styled (Style ().Width (800 ).Height (600 ));
448+ TextBlock (license);
449+ }
450+
451+ void ShowLicensesButton () {
452+ static bool licensesDialog {false };
453+ if (HyperlinkButton (" Show copyright notices" )) {
454+ licensesDialog = true ;
455+ }
456+ if (auto dialog = BeginContentDialog (&licensesDialog).Scoped ()) {
457+ ContentDialogTitle (" Copyright notices" );
458+ ShowLicenses ();
459+ const auto buttons = BeginContentDialogButtons ().Scoped ();
460+ ContentDialogCloseButton (" Close" ).Accent ();
461+ }
462+ }
463+
393464void ShowContent (Win32Window& window) {
394465 static const Style ContentLayoutStyle
395466 = Style ().FlexGrow (1 ).Gap (12 ).Margin (12 ).Padding (8 );
396467
397468 if (GetArtifacts ().empty ()) {
398469 window.SetResizeMode (Window::ResizeMode::Fixed, Window::ResizeMode::Fixed);
470+ const auto layout = BeginVStackPanel ().Styled (ContentLayoutStyle).Scoped ();
399471 Label (" Couldn't find anything from OpenKneeboard on your computer." )
400472 .Styled (ContentLayoutStyle);
473+ ShowLicensesButton ();
401474 return ;
402475 }
403476
404477 if (gCleanupMode != CleanupMode::Custom) {
405478 window.SetResizeMode (Window::ResizeMode::Fixed, Window::ResizeMode::Fixed);
406479 const auto layout = BeginVStackPanel ().Styled (ContentLayoutStyle).Scoped ();
407480 ShowModes ();
481+ ShowLicensesButton ();
408482 return ;
409483 }
410484
@@ -415,6 +489,7 @@ void ShowContent(Win32Window& window) {
415489 const auto layout = BeginVStackPanel ().Scoped ().Styled (ContentLayoutStyle);
416490 ShowModes ();
417491 ShowArtifacts ();
492+ ShowLicensesButton ();
418493}
419494
420495void AppTick (Win32Window& window) {
0 commit comments