Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🕝 Simple date/time visual added #34

Merged
merged 2 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ The following fields can be set in the `settings.json` root object:
| Name | Type | Description |
| --------------------------- | ------------------------ | ------------------------------------------ |
| `"showStartupNotification"` | `true` / `false` | Determines whether the "FlashCom is running" toast notification is shown on startup. |
| `"use24HourClock"` | `true` / `false` | Determines whether a 12-hour or 24-hour clock is shown. |
| `"commands"` | Array of Command Objects | The set of commands shown in FlashCom. |

## Commands
Expand Down
1 change: 1 addition & 0 deletions src/FlashCom/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace FlashCom
{
auto result{ m_settingsManager.LoadSettings() };
m_dataModel->ShowStartupNotification = m_settingsManager.GetShowStartupNotification();
m_dataModel->UseTwentyFourHourClock = m_settingsManager.UseTwentyFourHourClock();
m_dataModel->RootNode = m_settingsManager.GetCommandTreeRoot();
m_dataModel->CurrentNode = m_dataModel->RootNode.get();
if (!result.has_value())
Expand Down
1 change: 1 addition & 0 deletions src/FlashCom/Models/DataModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace FlashCom::Models
{
std::string LoadErrorMessage;
bool ShowStartupNotification;
bool UseTwentyFourHourClock;
std::shared_ptr<TreeNode> RootNode;
TreeNode* CurrentNode{ nullptr };
};
Expand Down
20 changes: 20 additions & 0 deletions src/FlashCom/Settings/SettingsManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace
constexpr std::string_view c_settingsFileName{ "settings.json" };
// JSON property names
constexpr std::string_view c_showStartupNotificationProperty{ "showStartupNotification" };
constexpr std::string_view c_useTwentyFourHourClockProperty{ "use24HourClock" };
constexpr std::string_view c_commandsJsonProperty{ "commands" };
constexpr std::string_view c_commandNameJsonProperty{ "name" };
constexpr std::string_view c_commandKeyJsonProperty{ "key" };
Expand All @@ -31,6 +32,7 @@ namespace
// Default settings.json contents
constexpr std::string_view c_defaultSettingsFileContents{ R"({
"showStartupNotification": true,
"use24HourClock": false,
"commands": [
{
"name": "README",
Expand Down Expand Up @@ -369,6 +371,11 @@ namespace FlashCom::Settings
return m_showStartupNotification;
}

bool SettingsManager::UseTwentyFourHourClock()
{
return m_useTwentyFourHourClock;
}

std::shared_ptr<Models::TreeNode> SettingsManager::GetCommandTreeRoot()
{
std::shared_lock settingsLock{ m_settingsAccessMutex };
Expand All @@ -393,6 +400,19 @@ namespace FlashCom::Settings
"Defaulting to '{}'.", c_showStartupNotificationProperty,
m_showStartupNotification);
}

if (settingsJson.contains(c_useTwentyFourHourClockProperty) &&
settingsJson.at(c_useTwentyFourHourClockProperty).is_boolean())
{
m_useTwentyFourHourClock =
settingsJson.at(c_useTwentyFourHourClockProperty).get<bool>();
}
else
{
SPDLOG_INFO("SettingsManager::PopulateSettingsValues - No '{}' bool property found. "
"Defaulting to '{}'.", c_useTwentyFourHourClockProperty,
m_useTwentyFourHourClock);
}
}

std::expected<void, std::string> SettingsManager::PopulateCommandTree(
Expand Down
2 changes: 2 additions & 0 deletions src/FlashCom/Settings/SettingsManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ namespace FlashCom::Settings
std::expected<void, std::string> LoadSettings();
std::filesystem::path GetSettingsFilePath();
bool GetShowStartupNotification();
bool UseTwentyFourHourClock();
std::shared_ptr<Models::TreeNode> GetCommandTreeRoot();

private:
std::filesystem::path const m_settingsFilePath;
std::shared_mutex m_settingsAccessMutex;
bool m_showStartupNotification{ true };
bool m_useTwentyFourHourClock{ false };
std::shared_ptr<Models::TreeNode> m_commandTreeRoot;

void PopulateSettingsValues(
Expand Down
103 changes: 103 additions & 0 deletions src/FlashCom/View/Ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ namespace
return root;
}

winrt::WUIC::ContainerVisual CreateClockContainer(winrt::WUIC::Compositor compositor)
{
auto clockContainer{ compositor.CreateContainerVisual() };
clockContainer.RelativeSizeAdjustment({ 1.0f, 1.0f });
clockContainer.Offset({ 0, 0, 0 });
return clockContainer;
}

winrt::WUIC::SpriteVisual CreateTextVisual(
FlashCom::View::CompositionManager& compositionManager,
winrt::MGCT::CanvasTextFormat textFormat, std::string_view content)
Expand Down Expand Up @@ -113,6 +121,17 @@ namespace

return spriteVisual;
}

std::chrono::milliseconds TimeToNextMinute()
{
auto const time = std::chrono::current_zone()
->to_local(std::chrono::system_clock::now());
auto minutes = std::chrono::floor<std::chrono::minutes>(time);
auto ms = std::chrono::floor<std::chrono::milliseconds>(time);
auto untilNextMinute = std::chrono::floor<std::chrono::milliseconds>(
std::chrono::minutes{ 1 } - (ms - minutes));
return untilNextMinute;
}
}

namespace FlashCom::View
Expand All @@ -127,6 +146,7 @@ namespace FlashCom::View
m_contentsVisual{ CreateContentsVisual(m_compositionManager.GetCompositor()) },
m_rootVisual{ CreateRootVisual(m_compositionManager.GetCompositor(), m_backgroundVisual,
m_contentsVisual) },
m_clockVisual{ CreateClockContainer(m_compositionManager.GetCompositor()) },
m_backgroundFadeInAnimation{
CreateBackgroundFadeInAnimation(m_compositionManager.GetCompositor()) },
m_contentScaleInAnimation{ CreateContentScaleInAnimation(
Expand All @@ -150,6 +170,12 @@ namespace FlashCom::View
void Ui::Hide()
{
m_hostWindow.Hide();
if (m_clockUpdateTimer)
{
SPDLOG_INFO("Ui::Hide - Canceling clock timer");
m_clockUpdateTimer.Cancel();
m_clockUpdateTimer = nullptr;
}
}

void Ui::Update(UpdateReasonKind reason)
Expand Down Expand Up @@ -204,13 +230,90 @@ namespace FlashCom::View
m_contentsVisual.Children().InsertAtTop(textVisual);
}

// Clock visual
UpdateClockVisual();
m_contentsVisual.Children().InsertAtTop(m_clockVisual);

// If we're showing, prepare to animate in
if (reason == UpdateReasonKind::Showing)
{
m_backgroundVisual.StartAnimation(L"opacity", m_backgroundFadeInAnimation);
m_contentsVisual.StartAnimation(L"opacity", m_backgroundFadeInAnimation);
m_contentsVisual.StartAnimation(L"scale", m_contentScaleInAnimation);

if (m_clockUpdateTimer)
{
m_clockUpdateTimer.Cancel();
m_clockUpdateTimer = nullptr;
}
auto nextUpdate{ TimeToNextMinute() };
SPDLOG_INFO("Ui::Update - setting clock timer for {}ms", nextUpdate.count());
m_clockUpdateTimer = winrt::WST::ThreadPoolTimer::CreateTimer(
{ this, &Ui::OnClockUpdateTimerElapsed }, nextUpdate);
}
}

void Ui::UpdateClockVisual()
{
static auto const locale{ std::locale("") };
auto const time = std::chrono::current_zone()
->to_local(std::chrono::system_clock::now());
auto days = std::chrono::floor<std::chrono::days>(time);
std::chrono::hh_mm_ss hhmmss{ std::chrono::floor<std::chrono::milliseconds>(time - days) };
std::string timeText;

if (m_dataModel->UseTwentyFourHourClock)
{
timeText = std::format(locale, "{:0>2}:{:0>2}", hhmmss.hours().count(),
hhmmss.minutes().count());
}
else
{
timeText = std::format(locale, "{}:{:0>2}", hhmmss.hours().count() % 12,
hhmmss.minutes().count());
}

winrt::MGCT::CanvasTextFormat timeTextFormat;
timeTextFormat.FontFamily(L"Segoe UI");
timeTextFormat.FontSize(128);
timeTextFormat.FontWeight(winrt::WUIT::FontWeights::Bold());
timeTextFormat.HorizontalAlignment(winrt::MGCT::CanvasHorizontalAlignment::Left);
auto timeVisual{ CreateTextVisual(m_compositionManager, timeTextFormat, timeText) };
timeVisual.AnchorPoint({ 0.0f, 0.0f });
timeVisual.RelativeOffsetAdjustment({ 0.0f, 0.0f, 0.0f });
timeVisual.Offset({ 28.0f, 0.0f, 0.0f });

auto dateText{ std::format(locale, "{:%x}", time) };
winrt::MGCT::CanvasTextFormat dateTextFormat;
dateTextFormat.FontFamily(L"Segoe UI");
dateTextFormat.FontSize(48);
dateTextFormat.FontWeight(winrt::WUIT::FontWeights::Light());
dateTextFormat.HorizontalAlignment(winrt::MGCT::CanvasHorizontalAlignment::Left);
auto dateVisual{ CreateTextVisual(m_compositionManager, dateTextFormat, dateText) };
dateVisual.AnchorPoint({ 0.0f, 0.0f });
dateVisual.RelativeOffsetAdjustment({ 0.0f, 0.0f, 0.0f });
dateVisual.Offset({ 32.0f, (timeVisual.Size().y - 24.0f), 0.0f });

m_clockVisual.Children().RemoveAll();
m_clockVisual.Children().InsertAtTop(timeVisual);
m_clockVisual.Children().InsertAtTop(dateVisual);

}

void Ui::OnClockUpdateTimerElapsed(winrt::WST::ThreadPoolTimer /*timer*/)
{
auto nextUpdate{ TimeToNextMinute() };
SPDLOG_INFO(
"Ui::OnClockUpdateTimerElapsed - Updating clock visual and resetting timer to {}ms",
nextUpdate.count());
UpdateClockVisual();
if (m_clockUpdateTimer)
{
m_clockUpdateTimer.Cancel();
m_clockUpdateTimer = nullptr;
}
m_clockUpdateTimer = winrt::WST::ThreadPoolTimer::CreateTimer(
{ this, &Ui::OnClockUpdateTimerElapsed }, nextUpdate);
}
#pragma endregion Public
}
8 changes: 8 additions & 0 deletions src/FlashCom/View/Ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,25 @@ namespace FlashCom::View
void Update(UpdateReasonKind reason);

private:
void UpdateClockVisual();
void OnClockUpdateTimerElapsed(winrt::Windows::System::Threading::ThreadPoolTimer timer);

HostWindow& m_hostWindow;
Models::DataModel const * const m_dataModel;
CompositionManager m_compositionManager;
std::pair<uint32_t, uint32_t> m_uiBounds;
// Container visuals
winrt::Windows::UI::Composition::ContainerVisual m_backgroundVisual{ nullptr };
winrt::Windows::UI::Composition::ContainerVisual m_contentsVisual{ nullptr };
winrt::Windows::UI::Composition::ContainerVisual m_rootVisual{ nullptr };
// Element visuals
winrt::Windows::UI::Composition::ContainerVisual m_clockVisual{ nullptr };
// Animations
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation
m_backgroundFadeInAnimation{ nullptr };
winrt::Windows::UI::Composition::Vector3KeyFrameAnimation
m_contentScaleInAnimation{ nullptr };
// Misc
winrt::Windows::System::Threading::ThreadPoolTimer m_clockUpdateTimer{ nullptr };
};
}