10 Commits

Author SHA1 Message Date
708371a3d1 ui stuff + clear notifications 2023-04-02 16:29:35 +02:00
537cd2cbdf tooltip time format 2023-03-26 22:35:30 +02:00
6b29c5e0b8 big time refactor + button styles 2023-03-26 22:23:49 +02:00
a7fb909174 Merge branch 'master' of https://gogs.theasuro.de/asuro/AsuroImgui 2023-03-26 17:30:00 +02:00
3ecaf255d7 timer stuff 2023-03-26 17:29:32 +02:00
Till Wübbers
d5709c237f fake drop shadow 2023-03-26 17:18:13 +02:00
132d493dc6 task editor 2023-03-17 19:25:03 +01:00
42930e7d5e timer fixes 2023-02-25 22:06:52 +01:00
81ee94e502 Merge branch 'master' of https://gogs.theasuro.de/asuro/AsuroImgui 2023-02-24 14:20:40 +01:00
df3de70398 bug fixes 2023-02-24 14:20:37 +01:00
12 changed files with 528 additions and 123 deletions

View File

@@ -26,7 +26,11 @@ AudioDevice::AudioDevice(IMMDevice* device, LPCWSTR deviceId) : device(device)
err = device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&meterInterface); err = device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&meterInterface);
isError(err, "Failed to get audio meter interface: "); isError(err, "Failed to get audio meter interface: ");
getVolumeLimit(volumeInterface, &minVolumeDb, &maxVolumeDb); if (volumeInterface != nullptr && meterInterface != nullptr)
{
getVolumeLimit(volumeInterface, &minVolumeDb, &maxVolumeDb);
isInitialized = true;
}
if (propertyStore) if (propertyStore)
{ {

View File

@@ -12,6 +12,7 @@
class AudioDevice { class AudioDevice {
public: public:
bool isInitialized = false;
IMMDevice* device = nullptr; IMMDevice* device = nullptr;
IAudioEndpointVolume* volumeInterface = nullptr; IAudioEndpointVolume* volumeInterface = nullptr;
IAudioMeterInformation* meterInterface = nullptr; IAudioMeterInformation* meterInterface = nullptr;
@@ -50,8 +51,9 @@ public:
bool showDisabledDevices = false; bool showDisabledDevices = false;
bool showChecklistExtras = false; bool showChecklistExtras = false;
std::vector<std::string> taskNames = {}; std::vector<std::string> taskNames = {};
std::unordered_map<std::string, std::vector<time_t>> tasks = {}; std::unordered_map<std::string, std::vector<std::chrono::year_month_day>> tasks = {};
std::vector<std::string> baseStationMacAdresses = {}; std::vector<std::string> baseStationMacAdresses = {};
bool baseStationShowConsole;
float timerDuration = 5.f * 60.f; float timerDuration = 5.f * 60.f;
float timerRepeatDuration = 2.f * 60.f; float timerRepeatDuration = 2.f * 60.f;
bool timerRepeating = false; bool timerRepeating = false;
@@ -78,9 +80,10 @@ public:
int checklistHighlightDurationDays = 3; int checklistHighlightDurationDays = 3;
std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>(); std::shared_ptr<AudioData> audioData = std::make_shared<AudioData>();
HoverTargetType hoverTargetType = HoverTargetType::HOVER_TARGET_NONE; HoverTargetType hoverTargetType = HoverTargetType::HOVER_TARGET_NONE;
time_t hoverTargetDay = 0; std::chrono::year_month_day hoverTargetDay = {};
TimerData timerData{}; TimerData timerData{};
std::mutex timerMutex{}; std::mutex timerMutex{};
bool lighthouseProcActive = false;
//ApplicationData(const ApplicationData&) = delete; //ApplicationData(const ApplicationData&) = delete;
//ApplicationData& operator=(const ApplicationData&) = delete; //ApplicationData& operator=(const ApplicationData&) = delete;

View File

@@ -21,8 +21,10 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <format> #include <format>
#include <ctime> using std::chrono::year_month_day;
#undef min
#undef max
#define MAX_FONT_PATH_LENGTH 2048 #define MAX_FONT_PATH_LENGTH 2048
#define SETTINGS_POPUP_NAME "settings_popup" #define SETTINGS_POPUP_NAME "settings_popup"
@@ -33,7 +35,7 @@ DrawData* gDrawData;
ApplicationData* gAppData; ApplicationData* gAppData;
bool justDocked = false; bool justDocked = false;
bool isHidden = false; bool isHidden = false;
time_t selectedDay = 0; year_month_day selectedDay = {};
int main() int main()
{ {
@@ -56,16 +58,6 @@ int main()
startImgui(callbacks, "Audio Thingy", 700, 400); startImgui(callbacks, "Audio Thingy", 700, 400);
} }
time_t getDayStartOf(time_t time)
{
tm localTime;
localtime_s(&localTime, &time);
localTime.tm_hour = 0;
localTime.tm_min = 0;
localTime.tm_sec = 0;
return mktime(&localTime);
}
void init(DrawData& drawData, ApplicationData& appData) void init(DrawData& drawData, ApplicationData& appData)
{ {
std::wstring appPath; std::wstring appPath;
@@ -94,12 +86,15 @@ void init(DrawData& drawData, ApplicationData& appData)
iconFontPath.append("\\remixicon.ttf"); iconFontPath.append("\\remixicon.ttf");
io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges); io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges);
// style
loadUiStyle();
// Start timer thread // Start timer thread
std::thread timerThread(updateTimer, std::ref(appData)); std::thread timerThread(updateTimer, std::ref(appData));
timerThread.detach(); timerThread.detach();
// Time // Time
selectedDay = getDayStartOf(std::time(nullptr)); selectedDay = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
initShell(drawData); initShell(drawData);
initSettings(drawData, appData); initSettings(drawData, appData);
@@ -108,6 +103,8 @@ void init(DrawData& drawData, ApplicationData& appData)
void draw(DrawData& drawData, ApplicationData& appData) void draw(DrawData& drawData, ApplicationData& appData)
{ {
const float panelGap = 0.f;
justDocked = false; justDocked = false;
appData.hoverTargetType = HoverTargetType::HOVER_TARGET_NONE; appData.hoverTargetType = HoverTargetType::HOVER_TARGET_NONE;
@@ -127,24 +124,25 @@ void draw(DrawData& drawData, ApplicationData& appData)
ImGui::SetNextWindowPos(ImVec2(0, customYCursor)); ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += checklistWindow(appData, std::format(" {} Checklist", ICON_CHECK_FILL).c_str()).y; customYCursor += checklistWindow(appData, std::format(" {} Checklist", ICON_CHECK_FILL).c_str()).y;
customYCursor += 5.; customYCursor += panelGap;
// Timer // Timer
ImGui::SetNextWindowPos(ImVec2(0, customYCursor)); ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x / 2.f, 0)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x / 2.f - 1.f, 0));
timerWindow(drawData, appData); float timerWindowHeight = timerWindow(drawData, appData).y;
// Base Stations // Base Stations
ImGui::SetNextWindowPos(ImVec2(viewportSize.x / 2.f, customYCursor)); ImGui::SetNextWindowPos(ImVec2(viewportSize.x / 2.f, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x / 2.f, 0)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x / 2.f - 1.f, 0));
customYCursor += baseStationWindow(appData).y; float baseStationWindowHeight = baseStationWindow(appData).y;
customYCursor += 5.; customYCursor += std::fmaxf(baseStationWindowHeight, timerWindowHeight);
customYCursor += panelGap;
// Playback Devices // Playback Devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor)); ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0)); ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, std::format(" {} Playback", ICON_HEADPHONE_FILL).c_str()).y; customYCursor += audioDeviceWindow(appData, appData.audioData->playbackDevices, std::format(" {} Playback", ICON_HEADPHONE_FILL).c_str()).y;
customYCursor += 5.; customYCursor += panelGap;
// Recording devices // Recording devices
ImGui::SetNextWindowPos(ImVec2(0, customYCursor)); ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
@@ -161,15 +159,11 @@ void draw(DrawData& drawData, ApplicationData& appData)
glfwSetWindowPos(drawData.window, monitorX + monitorW - drawData.window_size.x, monitorY + monitorH - drawData.window_size.y); glfwSetWindowPos(drawData.window, monitorX + monitorW - drawData.window_size.x, monitorY + monitorH - drawData.window_size.y);
} }
// Tooltip
if (appData.hoverTargetType == HoverTargetType::HOVER_TARGET_CHECKLIST_DAY) if (appData.hoverTargetType == HoverTargetType::HOVER_TARGET_CHECKLIST_DAY)
{ {
tm time_tm;
localtime_s(&time_tm, &appData.hoverTargetDay);
ImGui::BeginTooltip(); ImGui::BeginTooltip();
char timeStr[32]; ImGui::Text(std::format("{:%a, %d.%m}", std::chrono::sys_days{ appData.hoverTargetDay }).c_str());
strftime(timeStr, 32, "%d.%m", &time_tm);
ImGui::Text(timeStr);
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
} }
@@ -179,6 +173,12 @@ void cleanup(DrawData& drawData, ApplicationData& appData)
cleanupShell(drawData); cleanupShell(drawData);
} }
float getButtonWidth(const char* label)
{
ImVec2 labelSize = ImGui::CalcTextSize(label);
return labelSize.x + ImGui::GetStyle().FramePadding.x * 2.f;
}
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData) ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
{ {
ImVec2 size{}; ImVec2 size{};
@@ -214,12 +214,18 @@ ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
ImGui::Text("display: %.1f ms", drawData.frameTimeDisplay); ImGui::Text("display: %.1f ms", drawData.frameTimeDisplay);
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("Style"))
{
ImGui::ShowStyleEditor();
ImGui::EndMenu();
}
if (appData.settings.docked) if (appData.settings.docked)
{ {
ImVec2 availableSpace = ImGui::GetContentRegionAvail(); ImVec2 availableSpace = ImGui::GetContentRegionAvail();
ImVec2 cursorPos = ImGui::GetCursorPos(); ImVec2 cursorPos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - 47.); ImGui::SetCursorPosX(cursorPos.x + availableSpace.x - getButtonWidth(ICON_SUBTRACT_FILL) * 2.f);
if (ImGui::SmallButton(ICON_SUBTRACT_FILL)) if (ImGui::SmallButton(ICON_SUBTRACT_FILL))
{ {
@@ -252,7 +258,7 @@ bool customButton(const char* id_start, const char* id_end, const char* title, b
if (visible) if (visible)
{ {
ImGui::PushID(buttonId.c_str()); ImGui::PushID(buttonId.c_str());
result = ImGui::SmallButton(title); result = dropButton(title, {}, 2.f, true);
ImGui::PopID(); ImGui::PopID();
} }
else else
@@ -270,8 +276,9 @@ bool windowHeaderButton(const char* id)
ImVec2 windowMax = ImGui::GetWindowContentRegionMax(); ImVec2 windowMax = ImGui::GetWindowContentRegionMax();
ImGui::PopClipRect(); ImGui::PopClipRect();
ImGui::SetCursorPos({ windowMax.x - 20.f, tempPos.y - 28.f }); ImGuiStyle style = ImGui::GetStyle();
bool result = ImGui::SmallButton(id); ImGui::SetCursorPos({ windowMax.x - getButtonWidth(ICON_CLOSE_FILL), tempPos.y - style.WindowPadding.y - 22.f });
bool result = dropButton(id, ImVec2{}, 1.f, true);
ImGui::PushClipRect(tempRect.Min, tempRect.Max, false); ImGui::PushClipRect(tempRect.Min, tempRect.Max, false);
ImGui::SetCursorPos(tempPos); ImGui::SetCursorPos(tempPos);
@@ -279,26 +286,29 @@ bool windowHeaderButton(const char* id)
return result; return result;
} }
void drawChecklistDayLines(ApplicationData& appData, ImDrawList* drawList, float lineHeight, time_t day) void drawChecklistDayLines(ApplicationData& appData, ImDrawList* drawList, float lineHeight, year_month_day day)
{ {
auto& tasks = appData.settings.tasks; for (std::string& taskName : appData.settings.taskNames)
size_t totalTasks = appData.settings.taskNames.size();
size_t count = std::count_if(tasks.begin(), tasks.end(), [&](std::pair<std::string, std::vector<time_t>> t) { return std::any_of(t.second.begin(), t.second.end(), [&](time_t tt) { return tt == day; }); });
for (int i = 0; i < count; i++)
{ {
ImVec2 cursorPos = ImGui::GetCursorScreenPos(); ImVec2 cursorPos = ImGui::GetCursorScreenPos();
drawList->AddLine({ cursorPos.x, cursorPos.y }, { cursorPos.x, cursorPos.y + lineHeight }, ImColor(.4f, .9f, .3f), DAY_LINE_WIDTH); ImColor color;
ImGui::SetCursorScreenPos({ cursorPos.x + DAY_LINE_OFFSET, cursorPos.y }); auto& tasks = appData.settings.tasks[taskName];
}
for (int i = 0; i < max(0, totalTasks - count); i++) if (std::any_of(tasks.begin(), tasks.end(), [&](year_month_day taskDay) { return taskDay == day; }))
{ {
ImVec2 cursorPos = ImGui::GetCursorScreenPos(); color = ImColor{ .4f, .9f, .3f };
drawList->AddLine({ cursorPos.x, cursorPos.y }, { cursorPos.x, cursorPos.y + lineHeight }, ImColor(.1f, .3f, .05f), DAY_LINE_WIDTH); }
else
{
color = ImColor{ .1f, .3f, .05f };
}
drawList->AddLine({ cursorPos.x, cursorPos.y }, { cursorPos.x, cursorPos.y + lineHeight }, color, DAY_LINE_WIDTH);
ImGui::SetCursorScreenPos({ cursorPos.x + DAY_LINE_OFFSET, cursorPos.y }); ImGui::SetCursorScreenPos({ cursorPos.x + DAY_LINE_OFFSET, cursorPos.y });
} }
} }
void drawDayLineButton(ApplicationData& appData, ImDrawList* drawList, float lineHeight, time_t day, bool drawRect = true) void drawDayLineButton(ApplicationData& appData, ImDrawList* drawList, float lineHeight, year_month_day day, bool drawRect = true)
{ {
ImVec2 pos = ImGui::GetCursorScreenPos(); ImVec2 pos = ImGui::GetCursorScreenPos();
ImVec2 startPos = { pos.x - DAY_OUTLINE_SIZE, pos.y - DAY_OUTLINE_SIZE }; ImVec2 startPos = { pos.x - DAY_OUTLINE_SIZE, pos.y - DAY_OUTLINE_SIZE };
@@ -324,12 +334,22 @@ void drawDayLineButton(ApplicationData& appData, ImDrawList* drawList, float lin
} }
} }
void formatTime(std::chrono::system_clock::time_point time, char* buffer, size_t bufferSize) void setTimerDuration(ApplicationData& appData, int timerEndHours, int timerEndMinutes, tm& timeInfo)
{ {
const time_t tTime = std::chrono::system_clock::to_time_t(time); timeInfo.tm_hour = timerEndHours;
tm timeInfo; timeInfo.tm_min = timerEndMinutes;
localtime_s(&timeInfo, &tTime); TimePoint newEndTime = std::chrono::system_clock::from_time_t(mktime(&timeInfo));
std::strftime(buffer, bufferSize, "%H:%M", &timeInfo);
if (newEndTime < std::chrono::system_clock::now())
{
newEndTime += std::chrono::days(1);
}
else if (newEndTime > std::chrono::system_clock::now() + std::chrono::days(1))
{
newEndTime -= std::chrono::days(1);
}
appData.settings.timerDuration = std::chrono::duration_cast<std::chrono::seconds>(newEndTime - std::chrono::system_clock::now()).count();
} }
ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData) ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData)
@@ -358,12 +378,12 @@ ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData)
// Draw window // Draw window
if (ImGui::Begin(title.c_str(), 0, ImGuiWindowFlags_NoResize)) if (ImGui::Begin(title.c_str(), 0, ImGuiWindowFlags_NoResize))
{ {
dropShadowWindowTitle();
if (windowHeaderButton(ICON_SETTINGS_FILL)) if (windowHeaderButton(ICON_SETTINGS_FILL))
{ {
ImGui::OpenPopup(SETTINGS_POPUP_NAME); ImGui::OpenPopup(SETTINGS_POPUP_NAME);
} }
float timerDisplayMinutes = appData.settings.timerDuration / 60.f; float timerDisplayMinutes = appData.settings.timerDuration / 60.f;
if (timerData.isTimerActive) if (timerData.isTimerActive)
@@ -396,7 +416,7 @@ ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData)
} }
// Play/Reset // Play/Reset
if (ImGui::Button(timerData.isTimerActive ? ICON_RESTART_LINE : ICON_PLAY_FILL)) if (dropButton(timerData.isTimerActive ? ICON_RESTART_LINE : ICON_PLAY_FILL))
{ {
if (!timerData.isTimerActive) if (!timerData.isTimerActive)
{ {
@@ -407,6 +427,7 @@ ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData)
else else
{ {
timerData.isTimerActive = false; timerData.isTimerActive = false;
WinToast::instance()->clear();
} }
} }
@@ -414,52 +435,90 @@ ImVec2 timerWindow(DrawData& drawData, ApplicationData& appData)
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5.f); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5.f);
if (ImGui::Button(ICON_SUBTRACT_FILL)) if (dropButton(ICON_SUBTRACT_FILL))
{ {
appData.settings.timerDuration -= 60.f; appData.settings.timerDuration -= 60.f;
if (appData.settings.timerDuration < 0.) appData.settings.timerDuration = 0.f; if (appData.settings.timerDuration < 0.) appData.settings.timerDuration = 0.f;
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::PushItemWidth(35.f); const char* durationFormatStr = "%.0fm";
if (ImGui::InputFloat("##timer", &timerDisplayMinutes, 0, 0, "%.0fm")) ImGui::PushItemWidth(calcInputWidth(durationFormatStr, timerDisplayMinutes));
if (ImGui::DragFloat("##timer", &timerDisplayMinutes, .1f, 0.f, 1000.f, durationFormatStr))
{ {
appData.settings.timerDuration = timerDisplayMinutes * 60.f; appData.settings.timerDuration = timerDisplayMinutes * 60.f;
} }
dropShadow();
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_ADD_FILL)) if (dropButton(ICON_ADD_FILL))
{ {
appData.settings.timerDuration += 60.f; appData.settings.timerDuration += 60.f;
} }
ImGui::SameLine(); ImGui::SameLine();
char timeStr[32]; TimePoint endTime;
std::string timerText; const char* endTimeText;
if (timerData.isTimerActive && timerData.timerHasNotified)
if (timerData.isTimerActive)
{ {
formatTime(timerData.timerStartTimestamp + std::chrono::seconds((int)appData.settings.timerDuration), timeStr, sizeof(timeStr)); if (timerData.timerHasNotified)
timerText = std::format("Ended at {}", timeStr); {
endTime = timerData.timerStartTimestamp + std::chrono::seconds((int)appData.settings.timerDuration);
endTimeText = "Ended";
}
else
{
endTime = appData.timerData.timerStartTimestamp + std::chrono::seconds((int)appData.settings.timerDuration);
endTimeText = "Ends";
}
} }
else else
{ {
formatTime(std::chrono::system_clock::now() + std::chrono::seconds((int)appData.settings.timerDuration), timeStr, sizeof(timeStr)); endTime = std::chrono::system_clock::now() + std::chrono::seconds((int)appData.settings.timerDuration);
timerText = std::format("Ends at {}", timeStr); endTimeText = "Ends";
} }
float availWidth = ImGui::GetContentRegionMax().x - ImGui::GetCursorPosX(); const time_t tTime = std::chrono::system_clock::to_time_t(endTime);
float textWidth = ImGui::CalcTextSize(timerText.c_str()).x; tm timeInfo;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + availWidth - textWidth); localtime_s(&timeInfo, &tTime);
ImGui::Text(timerText.c_str()); int timerEndHours = timeInfo.tm_hour;
int timerEndMinutes = timeInfo.tm_min;
const char* hourInputFormat = "%d";
float hourDragWidth = calcInputWidth(hourInputFormat, timerEndHours);
const char* minuteInputFormat = "%02d";
float minuteDragWidth = calcInputWidth(minuteInputFormat, timerEndMinutes);
ImGuiStyle style = ImGui::GetStyle();
float requiredWidth = ImGui::CalcTextSize(endTimeText).x + style.ItemSpacing.x + hourDragWidth + style.ItemSpacing.x + minuteDragWidth + 2.f;
float spaceWidth = ImGui::GetContentRegionAvail().x - requiredWidth;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + spaceWidth);
ImGui::Text("%s", endTimeText);
ImGui::SameLine();
ImGui::SetNextItemWidth(hourDragWidth);
if (ImGui::DragInt("##timerEndHours", &timerEndHours, .1f, 0, 23, hourInputFormat, ImGuiInputTextFlags_CharsDecimal))
{
setTimerDuration(appData, timerEndHours, timerEndMinutes, timeInfo);
}
dropShadow();
ImGui::SameLine();
ImGui::SetNextItemWidth(minuteDragWidth);
if (ImGui::DragInt("##timerEndMinutes", &timerEndMinutes, .2f, 0, 59, minuteInputFormat, ImGuiInputTextFlags_CharsDecimal))
{
setTimerDuration(appData, timerEndHours, timerEndMinutes, timeInfo);
}
dropShadow();
} }
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME)) if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
{ {
// Loop // Loop
//float loopContentWidth = ImGui::GetFrameHeight() + 5.f + ImGui::CalcTextSize("Loop").x + 5.f + 30.f + 10.f;
//ImGui::SetCursorPosX(ImGui::GetWindowWidth() - loopContentWidth);
ImGui::Checkbox("Loop", &appData.settings.timerRepeating); ImGui::Checkbox("Loop", &appData.settings.timerRepeating);
ImGui::SameLine(); ImGui::SameLine();
@@ -484,18 +543,21 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
{ {
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize)) if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
{ {
dropShadowWindowTitle();
if (windowHeaderButton(ICON_SETTINGS_FILL)) if (windowHeaderButton(ICON_SETTINGS_FILL))
{ {
ImGui::OpenPopup(SETTINGS_POPUP_NAME); ImGui::OpenPopup(SETTINGS_POPUP_NAME);
} }
time_t today = getDayStartOf(std::time(nullptr)); // Previous days
ImDrawList* drawList = ImGui::GetWindowDrawList(); ImDrawList* drawList = ImGui::GetWindowDrawList();
float lineHeight = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2; float lineHeight = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2;
for (int pastDay = 3; pastDay >= 1; pastDay--) for (int pastDay = 3; pastDay >= 1; pastDay--)
{ {
time_t date = today - (60 * 60 * 24 * pastDay); TimePoint now = std::chrono::system_clock::now();
TimePoint selectedDay = now - std::chrono::days{ pastDay };
year_month_day date = std::chrono::floor<std::chrono::days>(selectedDay);
drawDayLineButton(appData, drawList, lineHeight, date); drawDayLineButton(appData, drawList, lineHeight, date);
drawChecklistDayLines(appData, drawList, lineHeight, date); drawChecklistDayLines(appData, drawList, lineHeight, date);
@@ -503,33 +565,41 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5.f); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5.f);
} }
// Current day
ImVec2 cursorPos = ImGui::GetCursorScreenPos(); ImVec2 cursorPos = ImGui::GetCursorScreenPos();
drawList->AddLine({ cursorPos.x, cursorPos.y - 2.f }, { cursorPos.x, cursorPos.y + lineHeight + 2.f }, IM_COL32_WHITE, 1.f); drawList->AddLine({ cursorPos.x, cursorPos.y - 2.f }, { cursorPos.x, cursorPos.y + lineHeight + 2.f }, IM_COL32_WHITE, 1.f);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.f); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.f);
year_month_day today = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());
drawDayLineButton(appData, drawList, lineHeight, today, false); drawDayLineButton(appData, drawList, lineHeight, today, false);
drawChecklistDayLines(appData, drawList, lineHeight, today); drawChecklistDayLines(appData, drawList, lineHeight, today);
// Checkboxes
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.f); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10.f);
auto selectedDayMatcher = [&](time_t t) { return getDayStartOf(t) == selectedDay; }; auto selectedDayMatcher = [&](year_month_day taskDay) { return taskDay == selectedDay; };
for (std::string& taskName : appData.settings.taskNames) for (std::string& taskName : appData.settings.taskNames)
{ {
if (!appData.settings.tasks.contains(taskName)) if (!appData.settings.tasks.contains(taskName))
{ {
appData.settings.tasks.insert({ taskName, std::vector<time_t>{} }); appData.settings.tasks.insert({ taskName, std::vector<year_month_day>{} });
} }
std::vector<time_t>& taskDates = appData.settings.tasks[taskName]; std::vector<year_month_day>& taskDates = appData.settings.tasks[taskName];
bool taskDone = std::any_of(taskDates.begin(), taskDates.end(), selectedDayMatcher); bool taskDone = std::any_of(taskDates.begin(), taskDates.end(), selectedDayMatcher);
bool highlightButton = false;
std::string hoverText = "";
if (taskDates.size() > 0)
{
year_month_day mostRecentDoneDate = *std::max_element(taskDates.begin(), taskDates.end());
std::chrono::days timeDiffDays = std::chrono::round<std::chrono::days>(std::chrono::sys_days{ today } - std::chrono::sys_days{ mostRecentDoneDate });
highlightButton = timeDiffDays > std::chrono::days{ appData.checklistHighlightDurationDays };
hoverText = std::format("{}", timeDiffDays.count());
}
time_t mostRecentDoneDate = *std::max_element(taskDates.begin(), taskDates.end());
double timeDiffSeconds = difftime(today, getDayStartOf(mostRecentDoneDate));
int timeDiffDays = timeDiffSeconds / (60 * 60 * 24);
bool highlightButton = timeDiffDays >= appData.checklistHighlightDurationDays;
if (highlightButton) if (highlightButton)
{ {
ImGui::PushStyleColor(ImGuiCol_FrameBg, { .5f, .2f, .15f, 0.54f }); ImGui::PushStyleColor(ImGuiCol_FrameBg, { .5f, .2f, .15f, 0.54f });
@@ -538,7 +608,8 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
} }
ImVec2 beforeCheckbox = ImGui::GetCursorScreenPos(); ImVec2 beforeCheckbox = ImGui::GetCursorScreenPos();
if (ImGui::Checkbox(taskName.c_str(), &taskDone)) std::string checkboxText = std::format("{}##taskcheck{}", taskName.c_str(), taskName.c_str());
if (checkboxWithDropShadow(checkboxText.c_str(), &taskDone))
{ {
if (taskDone) if (taskDone)
{ {
@@ -549,28 +620,56 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
std::erase_if(taskDates, selectedDayMatcher); std::erase_if(taskDates, selectedDayMatcher);
} }
} }
ImGui::SameLine(); ImGui::SameLine(0.f, 10.f);
if (highlightButton) if (highlightButton)
{ {
ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
ImVec2 afterCheckbox = ImGui::GetCursorScreenPos(); ImVec2 afterCheckbox = ImGui::GetCursorScreenPos();
std::string dayText = std::format("{}", timeDiffDays); float textWidth = ImGui::CalcTextSize(hoverText.c_str()).x;
float textWidth = ImGui::CalcTextSize(dayText.c_str()).x;
ImGui::SetCursorScreenPos({ beforeCheckbox.x + ImGui::GetFrameHeight() / 2.f - textWidth / 2.f, beforeCheckbox.y }); ImGui::SetCursorScreenPos({ beforeCheckbox.x + ImGui::GetFrameHeight() / 2.f - textWidth / 2.f, beforeCheckbox.y });
ImGui::PushStyleColor(ImGuiCol_Text, { 1.f, 1.f, 1.f, 1.f }); ImGui::PushStyleColor(ImGuiCol_Text, { 1.f, 1.f, 1.f, 1.f });
ImGui::Text(dayText.c_str()); ImGui::Text(hoverText.c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::SetCursorScreenPos(afterCheckbox); ImGui::SetCursorScreenPos(afterCheckbox);
} }
} }
// Popup
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME)) if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
{ {
ImGui::Text("Task Highlight Duration");
ImGui::SameLine();
ImGui::PushItemWidth(75.f); ImGui::PushItemWidth(75.f);
ImGui::DragInt("Task highlight duration", &appData.checklistHighlightDurationDays, .05f, 1, 7, "%d days"); ImGui::DragInt("##taskduration", &appData.checklistHighlightDurationDays, .05f, 1, 7, "%d days");
ImGui::PopItemWidth(); ImGui::PopItemWidth();
ImGui::Text("Tasks");
auto taskIterator = appData.settings.taskNames.begin();
while (taskIterator != appData.settings.taskNames.end())
{
size_t taskIndex = taskIterator - appData.settings.taskNames.begin();
std::string& taskName = *taskIterator;
taskName.reserve(128);
ImGui::InputText(std::format("##taskinput{}", taskIndex).c_str(), taskName.data(), taskName.capacity());
ImGui::SameLine();
if (ImGui::SmallButton(std::format("{}##deltask{}", ICON_CLOSE_FILL, taskIndex).c_str()))
{
taskIterator = appData.settings.taskNames.erase(taskIterator);
}
else
{
taskIterator++;
}
}
if (ImGui::Button(ICON_ADD_FILL))
{
appData.settings.taskNames.push_back("");
}
ImGui::EndPopup(); ImGui::EndPopup();
} }
} }
@@ -580,8 +679,21 @@ ImVec2 checklistWindow(ApplicationData& appData, const char* title)
return size; return size;
} }
void startBaseStationProc(const char* args) void startBaseStationProc(ApplicationData& appData, const char* args, DWORD flags = 0)
{ {
if (appData.lighthouseProcActive) return;
appData.lighthouseProcActive = true;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
HANDLE exeOutWrite;
HANDLE exeOutRead;
CreatePipe(&exeOutRead, &exeOutWrite, &saAttr, 0);
SetHandleInformation(exeOutRead, HANDLE_FLAG_INHERIT, 0);
// additional information // additional information
STARTUPINFOA si; STARTUPINFOA si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
@@ -589,30 +701,46 @@ void startBaseStationProc(const char* args)
// set the size of the structures // set the size of the structures
ZeroMemory(&si, sizeof(si)); ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si); si.cb = sizeof(si);
si.hStdOutput = exeOutWrite;
si.dwFlags = STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&pi, sizeof(pi));
// start the program up // start the program up
DWORD creationFlags = 0; CreateProcessA("lighthouse-v2-manager.exe", const_cast<LPSTR>(args), NULL, NULL, true, flags, NULL, NULL, &si, &pi);
#ifdef _DEBUG
creationFlags = CREATE_NEW_CONSOLE;
#endif
CreateProcessA("lighthouse-v2-manager.exe", const_cast<LPSTR>(args), NULL, NULL, FALSE, creationFlags, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
for (;;)
{
constexpr size_t BUFFER_SIZE = 4096;
char readBuffer[BUFFER_SIZE]{};
DWORD readCount = 0;
bool readSuccess = ReadFile(exeOutRead, readBuffer, BUFFER_SIZE, &readCount, NULL);
if (!readSuccess || readCount == 0) break;
std::cout << readBuffer << std::endl;
}
// Close process and thread handles. // Close process and thread handles.
CloseHandle(pi.hProcess); CloseHandle(pi.hProcess);
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
CloseHandle(exeOutWrite);
CloseHandle(exeOutRead);
appData.lighthouseProcActive = false;
} }
ImVec2 baseStationWindow(ApplicationData& appData) ImVec2 baseStationWindow(ApplicationData& appData)
{ {
if (ImGui::Begin(std::format("{} Base Stations", ICON_ALARM_WARNING_FILL).c_str(), 0, ImGuiWindowFlags_NoResize)) if (ImGui::Begin(std::format("{} Base Stations", ICON_ALARM_WARNING_FILL).c_str(), 0, ImGuiWindowFlags_NoResize))
{ {
dropShadowWindowTitle();
if (windowHeaderButton(ICON_SETTINGS_FILL)) if (windowHeaderButton(ICON_SETTINGS_FILL))
{ {
ImGui::OpenPopup(SETTINGS_POPUP_NAME); ImGui::OpenPopup(SETTINGS_POPUP_NAME);
} }
if (ImGui::Button("Wake")) if (dropButton("Wake"))
{ {
std::string params{ " on" }; std::string params{ " on" };
for (std::string& mac : appData.settings.baseStationMacAdresses) for (std::string& mac : appData.settings.baseStationMacAdresses)
@@ -620,10 +748,10 @@ ImVec2 baseStationWindow(ApplicationData& appData)
params.append(" "); params.append(" ");
params.append(mac.c_str()); params.append(mac.c_str());
} }
startBaseStationProc(params.c_str()); startBaseStationProc(appData, params.c_str(), appData.settings.baseStationShowConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Shutdown")) if (dropButton("Shutdown"))
{ {
std::string params{ " off" }; std::string params{ " off" };
for (std::string& mac : appData.settings.baseStationMacAdresses) for (std::string& mac : appData.settings.baseStationMacAdresses)
@@ -631,15 +759,16 @@ ImVec2 baseStationWindow(ApplicationData& appData)
params.append(" "); params.append(" ");
params.append(mac.c_str()); params.append(mac.c_str());
} }
startBaseStationProc(params.c_str()); startBaseStationProc(appData, params.c_str(), appData.settings.baseStationShowConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
} }
} }
if (ImGui::BeginPopup(SETTINGS_POPUP_NAME)) if (ImGui::BeginPopup(SETTINGS_POPUP_NAME))
{ {
ImGui::Checkbox("Show Console", &appData.settings.baseStationShowConsole);
if (ImGui::Button("Search")) if (ImGui::Button("Search"))
{ {
startBaseStationProc("discover"); startBaseStationProc(appData, "discover");
// TODO: parse stuff (annoying) // TODO: parse stuff (annoying)
} }
ImGui::Text(std::format("Known: {}", appData.settings.baseStationMacAdresses.size()).c_str()); ImGui::Text(std::format("Known: {}", appData.settings.baseStationMacAdresses.size()).c_str());
@@ -655,17 +784,20 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
{ {
if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize)) if (ImGui::Begin(title, 0, ImGuiWindowFlags_NoResize))
{ {
dropShadowWindowTitle();
if (windowHeaderButton(ICON_SETTINGS_FILL)) if (windowHeaderButton(ICON_SETTINGS_FILL))
{ {
ImGui::OpenPopup(SETTINGS_POPUP_NAME); ImGui::OpenPopup(SETTINGS_POPUP_NAME);
} }
if (ImGui::BeginTable("DeviceTable", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders | ImGuiTableFlags_NoSavedSettings)) if (ImGui::BeginTable("DeviceTable", 4, ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_NoSavedSettings))
{ {
ImGui::TableSetupColumn("Devices", ImGuiTableColumnFlags_WidthStretch, 3.); ImGui::TableSetupColumn("Devices", ImGuiTableColumnFlags_WidthStretch, 3.);
ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthStretch, 1.); ImGui::TableSetupColumn("Volume", ImGuiTableColumnFlags_WidthStretch, 1.);
ImGui::TableSetupColumn("Defaults", ImGuiTableColumnFlags_WidthFixed, 55.); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, getButtonWidth(ICON_MUSIC_2_FILL));
ImGui::TableHeadersRow(); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, getButtonWidth(ICON_PHONE_FILL));
dropShadowTableHeadersRow();
for (auto& dev : deviceList) for (auto& dev : deviceList)
{ {
@@ -684,29 +816,36 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(.7, .7, .7, 1.)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(.7, .7, .7, 1.));
} }
// Device Name
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
float yPosTemp = ImGui::GetCursorPosY();
const float yPosOffset = 4.f;
// Device Name
ImGui::SetCursorPosY(yPosTemp + yPosOffset);
ImGui::Text(dev.name.c_str()); ImGui::Text(dev.name.c_str());
ImGui::SetCursorPosY(yPosTemp);
// Volume // Volume
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE) if (dev.state == DEVICE_STATE_ACTIVE)
{ {
float startCursorY = ImGui::GetCursorPosY();
// Mute button // Mute button
ImGui::PushID(std::string("bn_mute_").append(deviceIdUtf8).c_str()); ImGui::PushID(std::string("bn_mute_").append(deviceIdUtf8).c_str());
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); ImGui::SetCursorPosY(yPosTemp + yPosOffset);
bool isDeviceMuted = isMuted(dev.volumeInterface); bool isDeviceMuted = isMuted(dev.volumeInterface);
if (ImGui::Button(isDeviceMuted ? ICON_VOLUME_MUTE_FILL : ICON_VOLUME_UP_FILL)) if (dropButton(isDeviceMuted ? ICON_VOLUME_MUTE_FILL : ICON_VOLUME_UP_FILL, {}, 2.f, true))
{ {
setMuted(dev.volumeInterface, !isDeviceMuted); setMuted(dev.volumeInterface, !isDeviceMuted);
} }
ImGui::PopStyleColor();
ImGui::PopID(); ImGui::PopID();
ImGui::SameLine(0, 2); ImGui::SameLine();
ImGui::SetCursorPosY(startCursorY);
// Meter // Meter
static std::array<float, 2> meterValues{}; static std::array<float, 2> meterValues{};
@@ -752,22 +891,31 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
} }
// Defaults // Defaults
const float circleSize = 5.f;
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE) if (dev.state == DEVICE_STATE_ACTIVE)
{ {
if (dev.isDefaultConsole) if (dev.isDefaultConsole)
{ {
drawCircle(5, IM_COL32(50, 50, 222, 255)); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + getButtonWidth(ICON_MUSIC_2_FILL) / 2.f - circleSize);
ImGui::SetCursorPosY(yPosTemp + yPosOffset);
drawCircle(circleSize, IM_COL32(50, 50, 222, 255));
} }
if (customButton("bn_d_", deviceIdUtf8.c_str(), ICON_MUSIC_2_FILL, !dev.isDefaultConsole)) if (customButton("bn_d_", deviceIdUtf8.c_str(), ICON_MUSIC_2_FILL, !dev.isDefaultConsole))
{ {
setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eConsole); setDefaultAudioDevice(*appData.audioData, dev.id.c_str(), ERole::eConsole);
} }
}
ImGui::SameLine(); ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE)
{
if (dev.isDefaultCommunication) if (dev.isDefaultCommunication)
{ {
drawCircle(5, IM_COL32(222, 50, 50, 255)); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + getButtonWidth(ICON_PHONE_FILL) / 2.f - circleSize);
ImGui::SetCursorPosY(yPosTemp + yPosOffset);
drawCircle(circleSize, IM_COL32(222, 50, 50, 255));
} }
if (customButton("bn_c_", deviceIdUtf8.c_str(), ICON_PHONE_FILL, !dev.isDefaultCommunication)) if (customButton("bn_c_", deviceIdUtf8.c_str(), ICON_PHONE_FILL, !dev.isDefaultCommunication))
{ {

View File

@@ -4,11 +4,14 @@
#include "ApplicationData.h" #include "ApplicationData.h"
#include <vector> #include <vector>
#include <chrono>
#define DAY_LINE_WIDTH 3.f #define DAY_LINE_WIDTH 3.f
#define DAY_LINE_OFFSET 5.f #define DAY_LINE_OFFSET 5.f
#define DAY_OUTLINE_SIZE 3.f #define DAY_OUTLINE_SIZE 3.f
typedef std::chrono::time_point<std::chrono::system_clock> TimePoint;
void init(DrawData& drawData, ApplicationData& customData); void init(DrawData& drawData, ApplicationData& customData);
void draw(DrawData& drawData, ApplicationData& customData); void draw(DrawData& drawData, ApplicationData& customData);
void cleanup(DrawData& drawData, ApplicationData& appData); void cleanup(DrawData& drawData, ApplicationData& appData);

View File

@@ -92,7 +92,10 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
audioDevice.isDefaultCommunication = wcscmp(defaultCommunicationId, deviceId) == 0; audioDevice.isDefaultCommunication = wcscmp(defaultCommunicationId, deviceId) == 0;
} }
deviceList.push_back(std::move(audioDevice)); if (audioDevice.isInitialized)
{
deviceList.push_back(std::move(audioDevice));
}
} }
CoTaskMemFree(deviceId); CoTaskMemFree(deviceId);

View File

@@ -56,16 +56,19 @@ void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* en
std::string task{}; std::string task{};
task.resize(MAX_TASK_NAME_LENGTH); task.resize(MAX_TASK_NAME_LENGTH);
time_t dayTimestamp; int year;
if (sscanf_s(line, "task=%lld %s", &dayTimestamp, &task[0], MAX_TASK_NAME_LENGTH)) unsigned int month;
unsigned int day;
if (sscanf_s(line, "task=%d-%u-%u %s", &year, &month, &day, &task[0], MAX_TASK_NAME_LENGTH))
{ {
std::chrono::year_month_day date = std::chrono::year_month_day{ std::chrono::year{year}, std::chrono::month{month}, std::chrono::day{day} };
if (settings->tasks.contains(task)) if (settings->tasks.contains(task))
{ {
settings->tasks[task].push_back(dayTimestamp); settings->tasks[task].push_back(date);
} }
else else
{ {
settings->tasks.insert({ task, std::vector{ dayTimestamp } }); settings->tasks.insert({ task, std::vector{ date } });
} }
} }
@@ -76,6 +79,13 @@ void settingsReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* en
settings->baseStationMacAdresses.push_back(baseStationMac); settings->baseStationMacAdresses.push_back(baseStationMac);
} }
// baseStationShowConsole
int baseStationShowConsole;
if (sscanf_s(line, "baseStationShowConsole=%i", &baseStationShowConsole))
{
settings->baseStationShowConsole = (bool)baseStationShowConsole;
}
float timerDuration; float timerDuration;
if (sscanf_s(line, "timerDuration=%f", &timerDuration)) if (sscanf_s(line, "timerDuration=%f", &timerDuration))
{ {
@@ -110,7 +120,7 @@ void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTex
{ {
for (auto& date : task.second) for (auto& date : task.second)
{ {
outBuf->appendf("task=%lld %s\n", date, task.first.c_str()); outBuf->appendf("task=%d-%u-%u %s\n", int{ date.year() }, unsigned int{ date.month() }, unsigned int{ date.day() }, task.first.c_str());
} }
} }
@@ -165,3 +175,71 @@ void setAutostart(bool newValue)
if (isError(hr, "Failed to delete autostart key: ")) return; if (isError(hr, "Failed to delete autostart key: ")) return;
} }
} }
void loadUiStyle()
{
ImGuiStyle& style = ImGui::GetStyle();
style.WindowPadding = { 10.f, 14.f };
style.WindowBorderSize = 0.f;
style.FramePadding = { 8.f, 4.f };
style.ItemSpacing = { 6.f, 4.f };
style.GrabRounding = 2.f;
style.PopupBorderSize = 4.f;
ImVec4* colors = style.Colors;
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.07f, 0.09f, 0.99f);
colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.04f, 0.07f, 1.00f);
colors[ImGuiCol_Border] = ImVec4(0.14f, 0.10f, 0.13f, 0.99f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.02f, 0.01f, 0.01f, 0.54f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.98f, 0.36f, 0.36f, 0.40f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.60f, 0.41f, 0.41f, 0.67f);
colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.48f, 0.16f, 0.16f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.02f, 0.02f, 0.02f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.06f, 0.05f, 0.06f, 0.99f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.98f, 0.61f, 0.26f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.88f, 0.38f, 0.24f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.98f, 0.41f, 0.26f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.21f, 0.11f, 0.17f, 1.00f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.30f, 0.15f, 0.25f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.47f, 0.23f, 0.39f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.46f, 0.19f, 0.12f, 0.67f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.69f, 0.29f, 0.19f, 0.80f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.98f, 0.41f, 0.26f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.45f, 0.43f, 0.50f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.75f, 0.24f, 0.10f, 0.78f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.75f, 0.24f, 0.10f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.98f, 0.41f, 0.26f, 0.20f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.98f, 0.41f, 0.26f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.98f, 0.41f, 0.26f, 0.95f);
colors[ImGuiCol_Tab] = ImVec4(0.09f, 0.02f, 0.07f, 0.86f);
colors[ImGuiCol_TabHovered] = ImVec4(0.28f, 0.15f, 0.24f, 1.00f);
colors[ImGuiCol_TabActive] = ImVec4(0.37f, 0.14f, 0.29f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.33f, 0.21f, 0.28f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.42f, 0.20f, 0.14f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.25f, 0.10f, 0.10f, 1.00f);
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}

View File

@@ -17,3 +17,5 @@ void settingsWriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTex
void applySettings(DrawData& drawData, ApplicationData& appData); void applySettings(DrawData& drawData, ApplicationData& appData);
void updateDocked(DrawData& drawData, ApplicationData& appData); void updateDocked(DrawData& drawData, ApplicationData& appData);
void setAutostart(bool newValue); void setAutostart(bool newValue);
void loadUiStyle();

View File

@@ -40,7 +40,7 @@ void updateTimer(ApplicationData& appData)
timerData.isTimerActive = false; timerData.isTimerActive = false;
messageText = L"Timer does not repeat."; messageText = L"Timer does not repeat.";
} }
showToastNotification(&timerData.toastHandler, titleText.c_str(), messageText.c_str()); showToastNotification(&timerData.toastHandler, titleText.c_str(), messageText.c_str(), appData.settings.timerRepeating ? appData.settings.timerRepeatDuration * 1000 : 0);
} }
else else
{ {

View File

@@ -82,15 +82,23 @@ void initShell(DrawData& drawData)
}); });
} }
void showToastNotification(TimerToastHandler* handler, const wchar_t* title, const wchar_t* text) void showToastNotification(TimerToastHandler* handler, const wchar_t* title, const wchar_t* text, UINT64 expiresInMs)
{ {
WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText02); WinToastTemplate templ = WinToastTemplate(WinToastTemplate::ImageAndText01);
templ.setScenario(WinToastTemplate::Scenario::Alarm);
templ.setImagePath(L"E:\\Code\\AsuroImgui\\x64\\Debug\\kaiju.ico"); templ.setImagePath(L"E:\\Code\\AsuroImgui\\x64\\Debug\\kaiju.ico");
templ.setTextField(title, WinToastTemplate::FirstLine); templ.setTextField(title, WinToastTemplate::FirstLine);
templ.setTextField(text, WinToastTemplate::SecondLine); templ.setAttributionText(text);
templ.addAction(L"Stop"); templ.addAction(L"Stop");
if (!WinToast::instance()->showToast(templ, handler)) { templ.addAction(L"Dismiss");
OutputDebugString(L"Error: Could not launch your toast notification!\n"); if (expiresInMs > 0)
{
templ.setExpiration(expiresInMs);
}
if (!WinToast::instance()->showToast(templ, handler))
{
OutputDebugString(L"Error: Could not launch toast!\n");
} }
} }
@@ -133,6 +141,14 @@ void TimerToastHandler::toastActivated() const
void TimerToastHandler::toastActivated(int actionIndex) const void TimerToastHandler::toastActivated(int actionIndex) const
{ {
if (actionIndex == 0)
{
gAppData->timerData.isTimerActive = false;
}
else if (actionIndex == 1)
{
}
} }
void TimerToastHandler::toastFailed() const void TimerToastHandler::toastFailed() const

View File

@@ -15,7 +15,7 @@ struct TimerToastHandler : IWinToastHandler
void initShell(DrawData& drawData); void initShell(DrawData& drawData);
void showToastNotification(TimerToastHandler* handler, const wchar_t* title, const wchar_t* text); void showToastNotification(TimerToastHandler* handler, const wchar_t* title, const wchar_t* text, UINT64 expiresInMs = 0);
void cleanupShell(DrawData& drawData); void cleanupShell(DrawData& drawData);

View File

@@ -1,6 +1,9 @@
#include "ImguiBase.h" #include "ImguiBase.h"
// hacky inlcude for custom button alignment
#include "imgui_internal.h"
#include "imgui_impl_glfw.h" #include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h" #include "imgui_impl_vulkan.h"
@@ -568,4 +571,121 @@ ImVec2 getWindowSize(GLFWwindow* window)
int window_height; int window_height;
glfwGetWindowSize(window, &window_width, &window_height); glfwGetWindowSize(window, &window_width, &window_height);
return ImVec2(window_width, window_height); return ImVec2(window_width, window_height);
} }
void dropShadow()
{
dropShadow(DEFAULT_DROP_SHADOW_SIZE, DEFAULT_DROP_SHADOW_SIZE, ImGui::GetStyle().Colors[DEFAULT_DROP_SHADOW_COLOR_ID]);
}
void dropShadow(const float shadowSizeX, const float shadowSizeY, const ImColor& color)
{
ImVec2 lastMin = ImGui::GetItemRectMin();
ImVec2 lastMax = ImGui::GetItemRectMax();
renderDropShadow(shadowSizeX, shadowSizeY, lastMin, lastMax, color);
}
void renderDropShadow(const float shadowSizeX, const float shadowSizeY, const ImVec2& min, const ImVec2& max, const ImColor& color)
{
ImDrawList* windowDrawList = ImGui::GetWindowDrawList();
if (shadowSizeY > 0.f)
{
windowDrawList->AddQuadFilled(
{ min.x, max.y },
{ max.x, max.y },
{ max.x + shadowSizeX, max.y + shadowSizeY },
{ min.x + shadowSizeX, max.y + shadowSizeY },
color);
}
if (shadowSizeX > 0.f)
{
windowDrawList->AddQuadFilled(
{ max.x, min.y },
{ max.x + shadowSizeX, min.y + shadowSizeY },
{ max.x + shadowSizeX, max.y + shadowSizeY },
{ max.x, max.y },
color);
}
}
void dropShadowTableHeadersRow()
{
ImGui::TableNextRow();
ImVec2 startPos = {};
for (int i = 0; i < ImGui::TableGetColumnCount(); i++)
{
ImGui::TableSetColumnIndex(i);
ImGui::TableHeader(ImGui::TableGetColumnName());
if (i == 0) startPos = ImGui::GetItemRectMin();
}
renderDropShadow(DEFAULT_DROP_SHADOW_SIZE, DEFAULT_DROP_SHADOW_SIZE, startPos, ImGui::GetItemRectMax(), ImGui::GetStyle().Colors[DEFAULT_DROP_SHADOW_COLOR_ID]);
}
void dropShadowWindowTitle()
{
ImRect tempRect = ImGui::GetCurrentWindow()->ClipRect;
ImGui::PopClipRect();
dropShadow(0., 4., { .2f, .2f, .2f, .5f });
ImGui::PushClipRect(tempRect.Min, tempRect.Max, false);
}
bool dropButton(const char* label, const ImVec2& size, float shadowSize, bool smallButton)
{
ImGuiStyle style = ImGui::GetStyle();
ImVec2 backupPadding = style.FramePadding;
if (smallButton) style.FramePadding.y = 0.f;
const ImVec2 labelSize = ImGui::CalcTextSize(label, NULL, true);
ImVec2 buttonMin = ImGui::GetCursorScreenPos();
ImVec2 buttonSize = size;
if (buttonSize.x <= 0.f) buttonSize.x = labelSize.x + style.FramePadding.x * 2.0f;
if (buttonSize.y <= 0.f) buttonSize.y = labelSize.y + style.FramePadding.y * 2.0f;
float baseLineOffset = ImGui::GetCurrentWindowRead()->DC.CurrLineTextBaseOffset;
if (smallButton && style.FramePadding.y < baseLineOffset) buttonMin.y += baseLineOffset - style.FramePadding.y;
bool result = ImGui::InvisibleButton(label, buttonSize);
bool held = ImGui::IsItemActive();
bool hovered = ImGui::IsItemHovered();
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
float maxOffset = std::fmaxf(shadowSize - 1.f, 0.f);
ImVec2 offset = held ? ImVec2{ maxOffset, maxOffset } : ImVec2{ 0.f, 0.f };
ImVec2 rectMin = buttonMin + offset;
ImVec2 rectMax = buttonMin + buttonSize + offset;
ImGui::RenderFrame(rectMin, rectMax, col, true, style.FrameRounding);
ImGui::RenderTextClipped(rectMin, rectMax, label, NULL, &labelSize, style.ButtonTextAlign);
float realShadowSize = held ? 1.f : shadowSize;
renderDropShadow(realShadowSize, realShadowSize, rectMin, rectMax, ImGui::GetStyle().Colors[DEFAULT_DROP_SHADOW_COLOR_ID]);
style.FramePadding = backupPadding;
return result;
}
bool checkboxWithDropShadow(const char* label, bool* v, const float shadowSize)
{
ImGuiStyle& style = ImGui::GetStyle();
float oldSpacingX = style.ItemInnerSpacing.x;
style.ItemInnerSpacing.x += shadowSize;
bool result = ImGui::Checkbox(label, v);
style.ItemInnerSpacing.x = oldSpacingX;
ImVec2 lastMin = ImGui::GetItemRectMin();
renderDropShadow(shadowSize, shadowSize, lastMin, lastMin + ImVec2{ ImGui::GetFrameHeight(), ImGui::GetFrameHeight() }, ImGui::GetStyle().Colors[DEFAULT_DROP_SHADOW_COLOR_ID]);
return result;
}
ImVec2 operator +(const ImVec2& a, const ImVec2& b) { return ImVec2{ a.x + b.x, a.y + b.y }; }
ImVec2 operator +(const ImVec2& a, const float s) { return ImVec2{ a.x + s, a.y + s }; }
ImVec2 operator -(const ImVec2& a, const ImVec2& b) { return ImVec2{ a.x - b.x, a.y - b.y }; }
ImVec2 operator -(const ImVec2& a, const float s) { return ImVec2{ a.x - s, a.y - s }; }
ImVec2 operator *(const ImVec2& a, float s) { return ImVec2{ a.x * s, a.y * s }; }
ImVec2 operator /(const ImVec2& a, float s) { return ImVec2{ a.x / s, a.y / s }; }

View File

@@ -57,3 +57,31 @@ public:
int startImgui(ImGuiCallbacks& callbacks, const char* title, int windowWidth, int windowHeight); int startImgui(ImGuiCallbacks& callbacks, const char* title, int windowWidth, int windowHeight);
ImVec2 getWindowSize(GLFWwindow* window); ImVec2 getWindowSize(GLFWwindow* window);
constexpr float DEFAULT_DROP_SHADOW_SIZE = 3.f;
constexpr ImGuiCol DEFAULT_DROP_SHADOW_COLOR_ID = ImGuiCol_TabUnfocused;
void dropShadow();
void dropShadow(const float shadowSizeX, const float shadowSizeY, const ImColor& color);
void renderDropShadow(const float shadowSizeX, const float shadowSizeY, const ImVec2& min, const ImVec2& max, const ImColor& color);
void dropShadowTableHeadersRow();
void dropShadowWindowTitle();
bool dropButton(const char* label, const ImVec2& size = ImVec2(0, 0), float shadowSize = DEFAULT_DROP_SHADOW_SIZE, bool smallButton = false);
bool checkboxWithDropShadow(const char* label, bool* v, const float shadowSize = DEFAULT_DROP_SHADOW_SIZE);
template <typename T>
float calcInputWidth(const char* formatStr, T value)
{
constexpr size_t MAX_BUFFER_SIZE = 256;
char formatStrBuffer[MAX_BUFFER_SIZE];
snprintf(formatStrBuffer, MAX_BUFFER_SIZE, formatStr, value);
return ImGui::CalcTextSize(formatStrBuffer).x + ImGui::GetStyle().FramePadding.x * 2.f;
}
ImVec2 operator +(const ImVec2& a, const ImVec2& b);
ImVec2 operator +(const ImVec2& a, const float s);
ImVec2 operator -(const ImVec2& a, const ImVec2& b);
ImVec2 operator -(const ImVec2& a, const float s);
ImVec2 operator *(const ImVec2& a, float s);
ImVec2 operator /(const ImVec2& a, float s);