Skip to content

Commit

Permalink
introduced notifications
Browse files Browse the repository at this point in the history
- generalized notifications triggered when filesystem changes detected into a system wide notification mechanism
- provides feedback on actions (like saving project, etc...)
  • Loading branch information
ypujante committed Aug 28, 2023
1 parent 29d3da4 commit d5d3a13
Show file tree
Hide file tree
Showing 11 changed files with 440 additions and 97 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ set(re-edit_BUILD_SOURCES
"${re-edit_CPP_SRC_DIR}/re/edit/Graphics.cpp"
"${re-edit_CPP_SRC_DIR}/re/edit/NetworkManager.h"
"${re-edit_CPP_SRC_DIR}/re/edit/NetworkManager.cpp"
"${re-edit_CPP_SRC_DIR}/re/edit/Notification.h"
"${re-edit_CPP_SRC_DIR}/re/edit/Notification.cpp"
"${re-edit_CPP_SRC_DIR}/re/edit/Panel.h"
"${re-edit_CPP_SRC_DIR}/re/edit/Panel.cpp"
"${re-edit_CPP_SRC_DIR}/re/edit/PanelActions.cpp"
Expand Down
171 changes: 115 additions & 56 deletions src/cpp/re/edit/AppContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,23 @@
#include "Utils.h"
#include "stl.h"
#include "Clipboard.h"
#include "UIContext.h"
#include <regex>
#include <efsw/efsw.hpp>
#include <nfd.h>
#include <version.h>

namespace re::edit {

constexpr auto kShortNotificationDuration = std::chrono::seconds(1);
constexpr auto kInfoNotificationDuration = std::chrono::seconds(5);

namespace impl {

class UpdateListener : public efsw::FileWatchListener
{
public:
UpdateListener(AppContext &iCtx, fs::path const &iRoot) :
UpdateListener(AppContext *iCtx, fs::path const &iRoot) :
fCtx{iCtx}, fRoot{fs::canonical(iRoot)}
{
// empty
Expand Down Expand Up @@ -70,8 +74,13 @@ class UpdateListener : public efsw::FileWatchListener

if(file == fRoot / "motherboard_def.lua" || file == fRoot / "info.lua")
{
// trigger maybe reloadDevice
fCtx.maybeReloadDevice(true);
if(UIContext::HasCurrent())
{
UIContext::GetCurrent().execute([ctx = fCtx] {
if(AppContext::IsCurrent(ctx))
ctx->onDeviceUpdate();
});
}
}
else
{
Expand All @@ -80,15 +89,20 @@ class UpdateListener : public efsw::FileWatchListener
std::cmatch m;
if(std::regex_search(file.filename().u8string().c_str(), m, FILENAME_REGEX))
{
// trigger maybe scanDirectory
fCtx.maybeReloadTextures(true);
if(UIContext::HasCurrent())
{
UIContext::GetCurrent().execute([ctx = fCtx] {
if(AppContext::IsCurrent(ctx))
ctx->onTexturesUpdate();
});
}
}
}
}
}

private:
AppContext &fCtx;
AppContext *fCtx;
fs::path fRoot;
};

Expand Down Expand Up @@ -120,6 +134,17 @@ AppContext::~AppContext()
disableFileWatcher();
}

//------------------------------------------------------------------------
// AppContext::IsCurrent
//------------------------------------------------------------------------
bool AppContext::IsCurrent(AppContext *iCtx)
{
if(iCtx && Application::HasCurrent())
return Application::GetCurrent().getAppContext() == iCtx;
else
return false;
}

//------------------------------------------------------------------------
// AppContext::initPanels
//------------------------------------------------------------------------
Expand Down Expand Up @@ -262,34 +287,6 @@ void AppContext::render()
renderZoomSelection();
renderGridSelection();

if(hasNotifications())
{
ImGui::SeparatorText("Notifications");
if(maybeReloadTextures())
{
ImGui::AlignTextToFramePadding();
ReGui::TipIcon();ImGui::SameLine();ImGui::TextUnformatted("Detected image changes");
ImGui::SameLine();
if(ImGui::Button(ReGui_Prefix(ReGui_Icon_RescanImages, "Rescan")))
fReloadTexturesRequested = true;
ImGui::SameLine();
if(ImGui::Button(ReGui_Prefix(ReGui_Icon_Reset, "Dismiss")))
maybeReloadTextures(false);
}

if(maybeReloadDevice())
{
ImGui::AlignTextToFramePadding();
ReGui::TipIcon();ImGui::SameLine();ImGui::TextUnformatted("Detected device changes");
ImGui::SameLine();
if(ImGui::Button(ReGui_Prefix(ReGui_Icon_ReloadMotherboard, "Reload")))
fReloadDeviceRequested = true;
ImGui::SameLine();
if(ImGui::Button(ReGui_Prefix(ReGui_Icon_Reset, "Dismiss")))
maybeReloadDevice(false);
}
}

ImGui::SeparatorText("Rendering");

ImGui::PushID("Rendering");
Expand Down Expand Up @@ -574,10 +571,10 @@ std::string AppContext::getDeviceName() const
//------------------------------------------------------------------------
// AppContext::reloadTextures
//------------------------------------------------------------------------
void AppContext::reloadTextures()
bool AppContext::reloadTextures()
{
markEdited();
checkForErrors();
return checkForErrors();
}

//------------------------------------------------------------------------
Expand Down Expand Up @@ -625,10 +622,10 @@ void AppContext::initGUI2D(Utils::CancellableSPtr const &iCancellable)
//------------------------------------------------------------------------
// AppContext::reloadDevice
//------------------------------------------------------------------------
void AppContext::reloadDevice()
bool AppContext::reloadDevice()
{
initDevice();
checkForErrors();
return checkForErrors();
}

//------------------------------------------------------------------------
Expand Down Expand Up @@ -819,30 +816,20 @@ void AppContext::renderMainMenu()
{
auto numTextures = importTexturesBlocking();
if(numTextures > 0)
Application::GetCurrent().newDialog("Import")
.text(fmt::printf("%ld images imported successfully", numTextures))
.buttonOk();
Application::GetCurrent().newNotification()
.text(fmt::printf("%ld image(s) imported successfully", numTextures))
.dismissAfter(kInfoNotificationDuration);
;
}
ImGui::Separator();
if(ImGui::MenuItem(ReGui_Prefix(ReGui_Icon_RescanImages, "Rescan images")))
{
fReloadTexturesRequested = true;
}
if(fMaybeReloadTextures)
{
ImGui::SameLine();
ImGui::TextUnformatted("\u00b7");
}
if(ImGui::MenuItem(ReGui_Prefix(ReGui_Icon_ReloadMotherboard, "Reload motherboard")))
{
fReloadDeviceRequested = true;
}
if(fMaybeReloadDevice)
{
ImGui::SameLine();
ImGui::TextUnformatted("\u00b7");
}
ImGui::Separator();
if(ImGui::MenuItem("Delete unused images"))
{
Expand Down Expand Up @@ -971,18 +958,36 @@ void AppContext::beforeRenderFrame()
if(fReloadTexturesRequested)
{
fReloadTexturesRequested = false;
fMaybeReloadTextures = false;
fTextureManager->scanDirectory();
reloadTextures();
if(reloadTextures())
{
Application::GetCurrent().newNotification()
.text("Images reloaded. Some errors detected.");
}
else
{
Application::GetCurrent().newNotification()
.text("Images reloaded successfully.")
.dismissAfter(kShortNotificationDuration);
}
}

if(fReloadDeviceRequested)
{
fReloadDeviceRequested = false;
fMaybeReloadDevice = false;
try
{
reloadDevice();
if(reloadDevice())
{
Application::GetCurrent().newNotification()
.text("Device reloaded. Some errors detected.");
}
else
{
Application::GetCurrent().newNotification()
.text("Device reloaded successfully.")
.dismissAfter(kShortNotificationDuration);
}
}
catch(...)
{
Expand Down Expand Up @@ -1041,13 +1046,21 @@ void AppContext::save()
Application::saveFile(GUI2D / "gui_2D.cmake", cmake(), &errors);
Application::GetCurrent().savePreferences(&errors);
if(errors.hasErrors())
{
Application::GetCurrent().newDialog("Error")
.preContentMessage("There were some errors during the save operation")
.lambda([errors] {
for(auto const &error: errors.getErrors())
ImGui::BulletText("%s", error.c_str());
})
.buttonOk();
}
else
{
Application::GetCurrent().newNotification()
.text("Project saved successfully")
.dismissAfter(kShortNotificationDuration);
}
// fAppContext->fUndoManager->clear();
fNeedsSaving = false;
fLastSavedUndoAction = fUndoManager->getLastUndoAction();
Expand Down Expand Up @@ -1182,7 +1195,7 @@ void AppContext::enableFileWatcher()
{
if(!fRootWatchID)
{
fRootListener = std::make_shared<impl::UpdateListener>(*this, fRoot);
fRootListener = std::make_shared<impl::UpdateListener>(this, fRoot);
fRootWatchID = fRootWatcher->addWatch(fRoot.u8string(), fRootListener.get(), true);
fRootWatcher->watch();
}
Expand Down Expand Up @@ -1840,6 +1853,52 @@ PanelType AppContext::getPanelType(Action const *iAction)
return PanelType::kUnknown;
}

//------------------------------------------------------------------------
// AppContext::onTexturesUpdate
//------------------------------------------------------------------------
void AppContext::onTexturesUpdate()
{
Application::GetCurrent()
.newUniqueNotification(ReGui::Notification::Key::from(&fReloadTexturesRequested))
.lambda([ctx = this]() {
if(!AppContext::IsCurrent(ctx))
{
return false;
}

ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Detected image changes");
if(ImGui::Button(ReGui_Prefix(ReGui_Icon_RescanImages, "Rescan")))
{
ctx->fReloadTexturesRequested = true;
}
return !ctx->fReloadTexturesRequested;
});
}

//------------------------------------------------------------------------
// AppContext::onDeviceUpdate
//------------------------------------------------------------------------
void AppContext::onDeviceUpdate()
{
Application::GetCurrent()
.newUniqueNotification(ReGui::Notification::Key::from(&fReloadDeviceRequested))
.lambda([ctx = this]() {
if(!AppContext::IsCurrent(ctx))
{
return false;
}

ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Detected device changes");
if(ImGui::Button(ReGui_Prefix(ReGui_Icon_ReloadMotherboard, "Reload")))
{
ctx->fReloadDeviceRequested = true;
}
return !ctx->fReloadDeviceRequested;
});
}

////------------------------------------------------------------------------
//// AppContext::onNativeWindowPositionChange
////------------------------------------------------------------------------
Expand Down
20 changes: 9 additions & 11 deletions src/cpp/re/edit/AppContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ class AppContext
AppContext(fs::path const &iRoot, std::shared_ptr<TextureManager> iTextureManager);
~AppContext();

static AppContext &GetCurrent() { RE_EDIT_INTERNAL_ASSERT(kCurrent != nullptr); return *kCurrent; }
inline static AppContext &GetCurrent() { RE_EDIT_INTERNAL_ASSERT(kCurrent != nullptr); return *kCurrent; }

/**
* @return `true` if and only if `iCtx` is not `nullptr` and the current app context is equal to `iCtx` */
static bool IsCurrent(AppContext *iCtx);

ImVec2 getCurrentPanelSize() const;
bool renderWidgetDefMenuItems(PanelType iPanelType, std::function<void(WidgetDef const &)> const &iAction);
Expand Down Expand Up @@ -215,12 +219,8 @@ class AppContext
friend class Widget;
friend class Application;

inline bool maybeReloadTextures() const { return fMaybeReloadTextures; }
inline bool maybeReloadDevice() const { return fMaybeReloadDevice; }
inline bool hasNotifications() const { return maybeReloadDevice() || maybeReloadTextures(); }

void maybeReloadTextures(bool b) { fMaybeReloadTextures = b; }
void maybeReloadDevice(bool b) { fMaybeReloadDevice = b; }
void onTexturesUpdate();
void onDeviceUpdate();

std::string getDeviceName() const;
constexpr bool hasFoldedPanels() const { return fHasFoldedPanels; }
Expand All @@ -246,7 +246,7 @@ class AppContext
protected:
void init(config::Device const &iConfig);
config::Device getConfig() const;
void reloadTextures();
bool reloadTextures();
void markEdited();
bool checkForErrors();
bool computeErrors();
Expand All @@ -256,7 +256,7 @@ class AppContext
void renderUndoHistory();
void initDevice();
void initGUI2D(Utils::CancellableSPtr const &iCancellable);
void reloadDevice();
bool reloadDevice();
void save();
void importBuiltIns(UserError *oErrors = nullptr);
void applyTextureEffects(UserError *oErrors = nullptr);
Expand Down Expand Up @@ -321,9 +321,7 @@ class AppContext
void *fLastUndoAction{};
bool fRecomputeDimensionsRequested{true};
bool fReloadTexturesRequested{};
std::atomic<bool> fMaybeReloadTextures{};
bool fReloadDeviceRequested{};
std::atomic<bool> fMaybeReloadDevice{};
std::optional<std::string> fNewLayoutRequested{};
ImGuiMouseCursor fMouseCursor{ImGuiMouseCursor_None};

Expand Down
Loading

0 comments on commit d5d3a13

Please sign in to comment.