diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/choice_enum.h | 54 | ||||
-rw-r--r-- | wx+/dc.h | 12 | ||||
-rw-r--r-- | wx+/focus.h | 12 | ||||
-rw-r--r-- | wx+/image_resources.cpp | 95 | ||||
-rw-r--r-- | wx+/image_resources.h | 7 | ||||
-rw-r--r-- | wx+/popup_dlg.cpp | 34 | ||||
-rw-r--r-- | wx+/popup_dlg_generated.h | 1 | ||||
-rw-r--r-- | wx+/taskbar.cpp | 19 | ||||
-rw-r--r-- | wx+/taskbar.h | 42 |
9 files changed, 195 insertions, 81 deletions
diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index f2c93927..e11b9991 100644 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -7,6 +7,7 @@ #ifndef CHOICE_ENUM_H_132413545345687 #define CHOICE_ENUM_H_132413545345687 +#include <unordered_map> #include <vector> #include <wx/choice.h> @@ -43,8 +44,11 @@ struct EnumDescrList descrList.push_back({ value, { text, tooltip } }); return *this; } + using DescrList = std::vector<std::pair<Enum, std::pair<wxString, wxString>>>; DescrList descrList; + + std::unordered_map<const wxChoice*, std::vector<wxString>> itemsSetLast; }; template <class Enum> void setEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value); template <class Enum> Enum getEnumVal(const EnumDescrList<Enum>& mapping, const wxChoice& ctrl); @@ -65,26 +69,34 @@ template <class Enum> void updateTooltipEnumVal(const EnumDescrList<Enum>& mappi //--------------- impelementation ------------------------------------------- template <class Enum> -void setEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value) +void setEnumVal(EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value) { - ctrl.Clear(); + auto& itemsSetLast = mapping.itemsSetLast[&ctrl]; - int selectedPos = 0; + std::vector<wxString> items; for (auto it = mapping.descrList.begin(); it != mapping.descrList.end(); ++it) + items.push_back(it->second.first); + + if (items != itemsSetLast) { - ctrl.Append(it->second.first); - if (it->first == value) - { - selectedPos = it - mapping.descrList.begin(); - - if (it->second.second.empty()) - ctrl.UnsetToolTip(); - else - ctrl.SetToolTip(it->second.second); - } + ctrl.Set(items); //expensive as fuck! => only call when absolutely needed! + itemsSetLast = std::move(items); } + //----------------------------------------------------------------- - ctrl.SetSelection(selectedPos); + const auto it = std::find_if(mapping.descrList.begin(), mapping.descrList.end(), [&](const auto& mapItem) { return mapItem.first == value; }); + if (it != mapping.descrList.end()) + { + if (const wxString& tooltip = it->second.second; + !tooltip.empty()) + ctrl.SetToolTip(tooltip); + else + ctrl.UnsetToolTip(); + + const int selectedPos = it - mapping.descrList.begin(); + ctrl.SetSelection(selectedPos); + } + else assert(false); } template <class Enum> @@ -103,11 +115,17 @@ Enum getEnumVal(const EnumDescrList<Enum>& mapping, const wxChoice& ctrl) template <class Enum> void updateTooltipEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl) { - const Enum currentValue = getEnumVal(mapping, ctrl); + const int selectedPos = ctrl.GetSelection(); - for (const auto& [enumValue, textAndTooltip] : mapping.descrList) - if (currentValue == enumValue) - ctrl.SetToolTip(textAndTooltip.second); + if (0 <= selectedPos && selectedPos < static_cast<int>(mapping.descrList.size())) + { + if (const auto& [text, tooltip] = mapping.descrList[selectedPos].second; + !tooltip.empty()) + ctrl.SetToolTip(tooltip); + else + ctrl.UnsetToolTip(); + } + else assert(false); } } @@ -73,8 +73,8 @@ class RecursiveDcClipper public: RecursiveDcClipper(wxDC& dc, const wxRect& r) : dc_(dc) { - auto it = refDcToAreaMap().find(&dc); - if (it != refDcToAreaMap().end()) + auto it = clippingAreas.find(&dc); + if (it != clippingAreas.end()) { oldRect_ = it->second; @@ -86,7 +86,7 @@ public: else { dc_.SetClippingRegion(r); - refDcToAreaMap().emplace(&dc_, r); + clippingAreas.emplace(&dc_, r); } } @@ -96,15 +96,15 @@ public: if (oldRect_) { dc_.SetClippingRegion(*oldRect_); - refDcToAreaMap()[&dc_] = *oldRect_; + clippingAreas[&dc_] = *oldRect_; } else - refDcToAreaMap().erase(&dc_); + clippingAreas.erase(&dc_); } private: //associate "active" clipping area with each DC - static std::unordered_map<wxDC*, wxRect>& refDcToAreaMap() { static std::unordered_map<wxDC*, wxRect> clippingAreas; return clippingAreas; } + inline static std::unordered_map<wxDC*, wxRect> clippingAreas; std::optional<wxRect> oldRect_; wxDC& dc_; diff --git a/wx+/focus.h b/wx+/focus.h index 4ba5f3f5..297d0754 100644 --- a/wx+/focus.h +++ b/wx+/focus.h @@ -24,6 +24,18 @@ bool isComponentOf(const wxWindow* child, const wxWindow* top) inline +wxWindow& getRootWindow(wxWindow& child) +{ + wxWindow* root = &child; + for (;;) + if (wxWindow* parent = root->GetParent()) + root = parent; + else + return *root; +} + + +inline wxTopLevelWindow* getTopLevelWindow(wxWindow* child) { for (wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent()) diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index 255a352e..5328b845 100644 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -83,21 +83,21 @@ ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const u } -auto getScalerTask(const wxString& name, const wxImage& img, int hqScale, Protected<std::vector<std::pair<std::wstring, ImageHolder>>>& result) +auto getScalerTask(const std::string& imageName, const wxImage& img, int hqScale, Protected<std::vector<std::pair<std::string, ImageHolder>>>& result) { - return [name = copyStringTo<std::wstring>(name), //don't trust wxString to be thread-safe like an int - width = img.GetWidth(), - height = img.GetHeight(), - dpiWidth = fastFromDIP(img.GetWidth()), - dpiHeight = fastFromDIP(img.GetHeight()), //don't call (wxWidgets function!) fastFromDIP() from worker thread - rgb = img.GetData(), - alpha = img.GetAlpha(), - hqScale, &result] + return [imageName, + width = img.GetWidth(), + height = img.GetHeight(), + dpiWidth = fastFromDIP(img.GetWidth()), + dpiHeight = fastFromDIP(img.GetHeight()), //don't call (wxWidgets function!) fastFromDIP() from worker thread + rgb = img.GetData(), + alpha = img.GetAlpha(), + hqScale, &result] { ImageHolder ih = dpiScale(width, height, dpiWidth, dpiHeight, rgb, alpha, hqScale); - result.access([&](std::vector<std::pair<std::wstring, ImageHolder>>& r) { r.emplace_back(name, std::move(ih)); }); + result.access([&](std::vector<std::pair<std::string, ImageHolder>>& r) { r.emplace_back(imageName, std::move(ih)); }); }; } @@ -109,19 +109,19 @@ public: ~DpiParallelScaler() { threadGroup_ = {}; } //DpiParallelScaler must out-live threadGroup!!! - void add(const wxString& name, const wxImage& img) + void add(const std::string& imageName, const wxImage& img) { imgKeeper_.push_back(img); //retain (ref-counted) wxImage so that the rgb/alpha pointers remain valid after passed to threads - threadGroup_->run(getScalerTask(name, img, hqScale_, result_)); + threadGroup_->run(getScalerTask(imageName, img, hqScale_, result_)); } - std::map<wxString, wxBitmap> waitAndGetResult() + std::map<std::string, wxBitmap> waitAndGetResult() { threadGroup_->wait(); - std::map<wxString, wxBitmap> output; + std::map<std::string, wxBitmap> output; - result_.access([&](std::vector<std::pair<std::wstring, ImageHolder>>& r) + result_.access([&](std::vector<std::pair<std::string, ImageHolder>>& r) { for (auto& [imageName, ih] : r) { @@ -137,7 +137,7 @@ public: private: const int hqScale_; std::vector<wxImage> imgKeeper_; - Protected<std::vector<std::pair<std::wstring, ImageHolder>>> result_; + Protected<std::vector<std::pair<std::string, ImageHolder>>> result_; using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>; std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") }; @@ -169,25 +169,25 @@ public: dpiScaler_.reset(); } - const wxBitmap& getImage (const wxString& name); - const wxAnimation& getAnimation(const wxString& name) const; + const wxBitmap& getImage (const std::string& name); + const wxAnimation& getAnimation(const std::string& name) const; private: GlobalBitmaps (const GlobalBitmaps&) = delete; GlobalBitmaps& operator=(const GlobalBitmaps&) = delete; - std::map<wxString, wxBitmap> bitmaps_; - std::map<wxString, wxAnimation> anims_; + std::map<std::string, wxBitmap> bitmaps_; + std::map<std::string, wxAnimation> anims_; std::unique_ptr<DpiParallelScaler> dpiScaler_; }; -void GlobalBitmaps::init(const Zstring& zipPath) +void GlobalBitmaps::init(const Zstring& zipPath) //throw FileError { assert(bitmaps_.empty() && anims_.empty()); - std::vector<std::pair<wxString /*file name*/, std::string /*byte stream*/>> streams; + std::vector<std::pair<Zstring /*file name*/, std::string /*byte stream*/>> streams; try //to load from ZIP first: { @@ -199,7 +199,7 @@ void GlobalBitmaps::init(const Zstring& zipPath) while (const auto& entry = std::unique_ptr<wxZipEntry>(zipStream.GetNextEntry())) //take ownership! if (std::string stream(entry->GetSize(), '\0'); !stream.empty() && zipStream.ReadAll(&stream[0], stream.size())) - streams.emplace_back(entry->GetName(), std::move(stream)); + streams.emplace_back(utfTo<Zstring>(entry->GetName()), std::move(stream)); else assert(false); } @@ -208,13 +208,11 @@ void GlobalBitmaps::init(const Zstring& zipPath) traverseFolder(beforeLast(zipPath, Zstr(".zip"), IF_MISSING_RETURN_NONE), [&](const FileInfo& fi) { if (endsWith(fi.fullPath, Zstr(".png"))) - try - { - std::string stream = loadBinContainer<std::string>(fi.fullPath, nullptr /*notifyUnbufferedIO*/); //throw FileError - streams.emplace_back(utfTo<wxString>(fi.itemName), std::move(stream)); - } - catch (FileError&) { assert(false); } - }, nullptr, nullptr, [](const std::wstring& errorMsg) { assert(false); }); //errors are not really critical in this context + { + std::string stream = loadBinContainer<std::string>(fi.fullPath, nullptr /*notifyUnbufferedIO*/); //throw FileError + streams.emplace_back(fi.itemName, std::move(stream)); + } + }, nullptr, nullptr, [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); } //-------------------------------------------------------------------- @@ -228,12 +226,11 @@ void GlobalBitmaps::init(const Zstring& zipPath) dpiScaler_ = std::make_unique<DpiParallelScaler>(hqScale); for (const auto& [fileName, stream] : streams) - { - wxMemoryInputStream wxstream(stream.c_str(), stream.size()); //stream does not take ownership of data - //bonus: work around wxWidgets bug: wxAnimation::Load() requires seekable input stream (zip-input stream is not seekable) - - if (endsWith(fileName, L".png")) + if (endsWith(fileName, Zstr(".png"))) { + wxMemoryInputStream wxstream(stream.c_str(), stream.size()); //stream does not take ownership of data + //bonus: work around wxWidgets bug: wxAnimation::Load() requires seekable input stream (zip-input stream is not seekable) + wxImage img(wxstream, wxBITMAP_TYPE_PNG); assert(img.IsOk()); @@ -241,13 +238,17 @@ void GlobalBitmaps::init(const Zstring& zipPath) //=> there's only one type of wxImage: with alpha channel, no mask!!! convertToVanillaImage(img); + const std::string imageName = utfTo<std::string>(beforeLast(fileName, Zstr("."), IF_MISSING_RETURN_NONE)); if (dpiScaler_) - dpiScaler_->add(fileName, img); //scale in parallel! + dpiScaler_->add(imageName, img); //scale in parallel! else - bitmaps_.emplace(fileName, img); + bitmaps_.emplace(imageName, img); + + //alternative: wxBitmap::NewFromPNGData(stream.c_str(), stream.size())? + // => Windows: just a (slow!) wrapper for wxBitmpat(wxImage())! } #if 0 - else if (endsWith(name, L".gif")) + else if (endsWith(fileName, Zstr(".gif"))) { [[maybe_unused]] const bool loadSuccess = anims_[fileName].Load(wxstream, wxANIMATION_TYPE_GIF); assert(loadSuccess); @@ -255,11 +256,10 @@ void GlobalBitmaps::init(const Zstring& zipPath) #endif else assert(false); - } } -const wxBitmap& GlobalBitmaps::getImage(const wxString& name) +const wxBitmap& GlobalBitmaps::getImage(const std::string& name) { //test: this function is first called about 220ms after GlobalBitmaps::init() has ended // => should be enough time to finish xBRZ scaling in parallel (which takes 50ms) @@ -270,18 +270,19 @@ const wxBitmap& GlobalBitmaps::getImage(const wxString& name) dpiScaler_.reset(); } - auto it = bitmaps_.find(contains(name, L'.') ? name : name + L".png"); //assume .png ending if nothing else specified - if (it != bitmaps_.end()) + if (auto it = bitmaps_.find(name); + it != bitmaps_.end()) return it->second; + assert(false); return wxNullBitmap; } -const wxAnimation& GlobalBitmaps::getAnimation(const wxString& name) const +const wxAnimation& GlobalBitmaps::getAnimation(const std::string& name) const { - auto it = anims_.find(contains(name, L'.') ? name : name + L".gif"); - if (it != anims_.end()) + if (auto it = anims_.find(name); + it != anims_.end()) return it->second; assert(false); return wxNullAnimation; @@ -307,7 +308,7 @@ void zen::cleanupResourceImages() } -const wxBitmap& zen::getResourceImage(const wxString& name) +const wxBitmap& zen::getResourceImage(const std::string& name) { if (std::shared_ptr<GlobalBitmaps> inst = GlobalBitmaps::instance()) return inst->getImage(name); @@ -316,7 +317,7 @@ const wxBitmap& zen::getResourceImage(const wxString& name) } -const wxAnimation& zen::getResourceAnimation(const wxString& name) +const wxAnimation& zen::getResourceAnimation(const std::string& name) { if (std::shared_ptr<GlobalBitmaps> inst = GlobalBitmaps::instance()) return inst->getAnimation(name); diff --git a/wx+/image_resources.h b/wx+/image_resources.h index ff1d5648..b996d977 100644 --- a/wx+/image_resources.h +++ b/wx+/image_resources.h @@ -14,11 +14,12 @@ namespace zen { -void initResourceImages(const Zstring& zipPath); //pass resources .zip file at application startup +//pass resources .zip file at application startup +void initResourceImages(const Zstring& zipPath); //throw FileError void cleanupResourceImages(); -const wxBitmap& getResourceImage (const wxString& name); -const wxAnimation& getResourceAnimation(const wxString& name); +const wxBitmap& getResourceImage (const std::string& name); +const wxAnimation& getResourceAnimation(const std::string& name); } #endif //IMAGE_RESOURCES_H_8740257825342532457 diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index c677cf57..2011a228 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -7,10 +7,11 @@ #include "popup_dlg.h" #include <wx/app.h> #include <wx/display.h> -#include <wx+/std_button_layout.h> -#include <wx+/font_size.h> -#include <wx+/image_resources.h> +#include "font_size.h" +#include "image_resources.h" #include "popup_dlg_generated.h" +#include "std_button_layout.h" +#include "taskbar.h" using namespace zen; @@ -75,6 +76,26 @@ public: buttonToDisableWhenChecked_(cfg.buttonToDisableWhenChecked) { + + if (type != DialogInfoType::info) + try + { + taskbar_ = std::make_unique<Taskbar>(parent); //throw TaskbarNotAvailable + switch (type) + { + case DialogInfoType::info: + break; + case DialogInfoType::warning: + taskbar_->setStatus(Taskbar::STATUS_WARNING); + break; + case DialogInfoType::error: + taskbar_->setStatus(Taskbar::STATUS_ERROR); + break; + } + } + catch (const TaskbarNotAvailable&) {} + + wxBitmap iconTmp; wxString titleTmp; switch (type) @@ -82,14 +103,14 @@ public: case DialogInfoType::info: //"Information" is meaningless as caption text! //confirmation doesn't use info icon - //iconTmp = getResourceImage(L"msg_info"); + //iconTmp = getResourceImage("msg_info"); break; case DialogInfoType::warning: - iconTmp = getResourceImage(L"msg_warning"); + iconTmp = getResourceImage("msg_warning"); titleTmp = _("Warning"); break; case DialogInfoType::error: - iconTmp = getResourceImage(L"msg_error"); + iconTmp = getResourceImage("msg_error"); titleTmp = _("Error"); break; } @@ -267,6 +288,7 @@ private: bool* checkBoxValue_; const QuestionButton2 buttonToDisableWhenChecked_; + std::unique_ptr<Taskbar> taskbar_; }; //######################################################################################## diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h index 2b09289b..33cc4872 100644 --- a/wx+/popup_dlg_generated.h +++ b/wx+/popup_dlg_generated.h @@ -68,4 +68,3 @@ public: ~PopupDialogGenerated(); }; - diff --git a/wx+/taskbar.cpp b/wx+/taskbar.cpp new file mode 100644 index 00000000..2cc31504 --- /dev/null +++ b/wx+/taskbar.cpp @@ -0,0 +1,19 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#include "taskbar.h" + + +using namespace zen; + + +class Taskbar::Impl {}; + +Taskbar::Taskbar(wxWindow* window) { throw TaskbarNotAvailable(); } +Taskbar::~Taskbar() {} + +void Taskbar::setStatus(Status status) {} +void Taskbar::setProgress(double fraction) {} diff --git a/wx+/taskbar.h b/wx+/taskbar.h new file mode 100644 index 00000000..985d89b4 --- /dev/null +++ b/wx+/taskbar.h @@ -0,0 +1,42 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef TASKBAR_H_98170845709124456 +#define TASKBAR_H_98170845709124456 + +#include <memory> +#include <wx/window.h> + + +namespace zen +{ +class TaskbarNotAvailable {}; + +class Taskbar +{ +public: + Taskbar(wxWindow* window); //throw TaskbarNotAvailable + ~Taskbar(); + + enum Status + { + STATUS_NORMAL, + STATUS_INDETERMINATE, + STATUS_WARNING, + STATUS_ERROR, + STATUS_PAUSED + }; + + void setStatus(Status status); //noexcept + void setProgress(double fraction); //between [0, 1]; noexcept + +private: + class Impl; + const std::unique_ptr<Impl> pimpl_; +}; +} + +#endif //TASKBAR_H_98170845709124456 |