Skip to content

Commit 73614b4

Browse files
committed
Gui: support application icon override
Add logic for dynamic changing icons in BitmapFactory, Command, TreeWidget, and OverlayManager. Add option 'Preference -> General -> Icon set' for user to select different icon set. 'Icon set' conf files are normal text files stored in <UserAppDataDir>/Gui/IconSets/. Add 'Tools -> Icon browser' to help creating user defined icon set. Icon set changes is handled by MainWindow by handling QEvent::StyleChange. Preference 'Icon set' configuration is set through MainWindow::setOverrideExtraIcons(). Stylesheet can also override icon by setting MainWindow property 'overrideIcons' as shown below (iconset: is in <UserAppDataDir>/Gui/IconSets/). It takes precedenece over 'Icon set'. Gui--MainWindow { qproperty-overrideIcons : url( TreeItemVisible, iconset:MyIcons/TreeItemVisible.svg TreeItemInvisible, iconset:MyIcons/TreeItemInvisible.svg );} @StudioPetrikas
1 parent 2a2008b commit 73614b4

24 files changed

Lines changed: 1426 additions & 326 deletions

src/Gui/Action.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -945,14 +945,14 @@ void WorkbenchTabBar::updateWorkbenches()
945945
else if (tab->tabData(i).toString() == action->objectName())
946946
changed = false;
947947

948-
if (changed) {
949-
tab->setTabIcon(i, action->icon());
950-
tab->setTabData(i, action->objectName());
951-
tab->setTabToolTip(i,
952-
Action::createToolTip(action->toolTip(),
948+
tab->setTabIcon(i, action->icon());
949+
tab->setTabToolTip(i,
950+
Action::createToolTip(action->toolTip(),
953951
action->text(),
954952
action->font(),
955953
action->shortcut().toString(QKeySequence::NativeText)));
954+
if (changed) {
955+
tab->setTabData(i, action->objectName());
956956
}
957957
if (showText) {
958958
if (tab->tabText(i) != action->text())

src/Gui/Application.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,7 +1628,7 @@ QPixmap Application::workbenchIcon(const QString& wb) const
16281628
if (pcWorkbench) {
16291629
// make a unique icon name
16301630
std::stringstream str;
1631-
str << static_cast<const void *>(pcWorkbench) << std::ends;
1631+
str << "Icon_" << wb.toLatin1().constData();
16321632
std::string iconName = str.str();
16331633
QPixmap icon;
16341634
if (BitmapFactory().findPixmapInCache(iconName.c_str(), icon))
@@ -2146,6 +2146,11 @@ void postAppSetup()
21462146
+ "Gui/Stylesheets/menu").c_str());
21472147
QDir::setSearchPaths(QString::fromLatin1("qssm"), qssMenuPaths);
21482148

2149+
QStringList iconSetPaths;
2150+
iconSetPaths << QString::fromUtf8((App::Application::getUserAppDataDir()
2151+
+ "Gui/IconSets").c_str());
2152+
QDir::setSearchPaths(QString::fromLatin1("iconset"), iconSetPaths);
2153+
21492154
// set search paths for images
21502155
QStringList imagePaths;
21512156
imagePaths << QString::fromUtf8((App::Application::getUserAppDataDir() + "Gui/images").c_str())
@@ -2197,7 +2202,12 @@ void postAppSetup()
21972202
#else
21982203
// Option to opt-out from using a Linux desktop icon theme.
21992204
// https://forum.freecadweb.org/viewtopic.php?f=4&t=35624
2200-
bool themePaths = hTheme->GetBool("ThemeSearchPaths",true);
2205+
//
2206+
// bool themePaths = hTheme->GetBool("ThemeSearchPaths",true);
2207+
//
2208+
// Disable system theme by default, as it rarely works with FreeCAD, because
2209+
// there are so many icons can't be find in common themes.
2210+
bool themePaths = hTheme->GetBool("_ThemeSearchPaths",false);
22012211
if (!themePaths) {
22022212
QStringList searchPaths;
22032213
searchPaths.prepend(QString::fromUtf8(":/icons"));
@@ -2618,11 +2628,32 @@ void Application::setStyleSheet(const QString& qssFile, bool tiledBackground)
26182628
qApp->setPalette(newPal);
26192629
}
26202630

2621-
26222631
QString current = mw->property("fc_currentStyleSheet").toString();
26232632
mw->setProperty("fc_currentStyleSheet", qssFile);
26242633

2625-
if (!qssFile.isEmpty() && current != qssFile) {
2634+
auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/MainWindow");
2635+
QString iconSet = QString::fromUtf8(hGrp->GetASCII("IconSet").c_str());
2636+
if (!iconSet.isEmpty()) {
2637+
QString prefix(QLatin1String("iconset:"));
2638+
QFile f;
2639+
if (QFile::exists(iconSet)) {
2640+
f.setFileName(iconSet);
2641+
}
2642+
else if (QFile::exists(prefix + iconSet)) {
2643+
f.setFileName(prefix + iconSet);
2644+
}
2645+
2646+
if (!f.fileName().isEmpty() && f.open(QFile::ReadOnly | QFile::Text)) {
2647+
QTextStream str(&f);
2648+
getMainWindow()->setOverrideExtraIcons(str.readAll());
2649+
}
2650+
}
2651+
2652+
// Icon set change is also triggered by style change, so we'll set
2653+
// stylesheet regardless of changes
2654+
//
2655+
// if (!qssFile.isEmpty() && current != qssFile) {
2656+
if (!qssFile.isEmpty()) {
26262657
// Search for stylesheet in user-defined search paths.
26272658
// For qss they are set-up in runApplication() with the prefix "qss"
26282659
QString prefix(QLatin1String("qss:"));

src/Gui/Application.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,10 @@ class GuiExport Application
291291
static PyObject* sAddIconPath (PyObject *self,PyObject *args); // adds a path to an icon file
292292
static PyObject* sAddIcon (PyObject *self,PyObject *args); // adds an icon to the cache
293293
static PyObject* sGetIcon (PyObject *self,PyObject *args); // get an icon from the cache
294+
static PyObject* sGetIconContext (PyObject *self,PyObject *args);
295+
static PyObject* sAddIconContext (PyObject *self,PyObject *args);
294296
static PyObject* sIsIconCached (PyObject *self,PyObject *args); // check if an icon is cached
297+
static PyObject* sGetIconNames (PyObject *self,PyObject *args); // get all cached icon names
295298

296299
static PyObject* sSendActiveView (PyObject *self,PyObject *args);
297300
static PyObject* sSendFocusView (PyObject *self,PyObject *args);

src/Gui/ApplicationPy.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
# include <Inventor/SoInput.h>
3434
# include <Inventor/actions/SoGetPrimitiveCountAction.h>
3535
# include <Inventor/nodes/SoSeparator.h>
36+
# include <QInputDialog>
3637
#endif
3738

3839
#include <xercesc/util/XMLString.hpp>
@@ -110,11 +111,20 @@ PyMethodDef Application::Methods[] = {
110111
"addIcon(string, string or list) -> None\n\n"
111112
"Add an icon as file name or in XPM format to the system"},
112113
{"getIcon", (PyCFunction) Application::sGetIcon, METH_VARARGS,
113-
"getIcon(string) -> QIcon\n\n"
114+
"getIcon(key:string, original=False) -> QIcon\n\n"
114115
"Get an icon in the system"},
116+
{"getIconContext", (PyCFunction) Application::sGetIconContext, METH_VARARGS,
117+
"getIconContext(string) -> list(string)\n\n"
118+
"Get user defined icon usage context"},
119+
{"addIconContext", (PyCFunction) Application::sAddIconContext, METH_VARARGS,
120+
"addIconContext(string) -> None\n\n"
121+
"Set user defined icon usage context"},
115122
{"isIconCached", (PyCFunction) Application::sIsIconCached, METH_VARARGS,
116123
"isIconCached(String) -> Bool\n\n"
117124
"Check if an icon with the given name is cached"},
125+
{"getIconNames", (PyCFunction) Application::sGetIconNames, METH_VARARGS,
126+
"getIconNames() -> list(string)\n\n"
127+
"Get all cached icon names in the system"},
118128
{"getMainWindow", (PyCFunction) Application::sGetMainWindow, METH_VARARGS,
119129
"getMainWindow() -> QMainWindow\n\n"
120130
"Return the main window instance"},
@@ -1289,15 +1299,40 @@ PyObject* Application::sAddIcon(PyObject * /*self*/, PyObject *args)
12891299
PyObject* Application::sGetIcon(PyObject * /*self*/, PyObject *args)
12901300
{
12911301
char *iconName;
1292-
if (!PyArg_ParseTuple(args, "s", &iconName))
1302+
PyObject *original = Py_False;
1303+
if (!PyArg_ParseTuple(args, "s|O", &iconName, &original))
12931304
return NULL;
12941305

12951306
PythonWrapper wrap;
12961307
wrap.loadGuiModule();
12971308
wrap.loadWidgetsModule();
1298-
auto pixmap = BitmapFactory().pixmap(iconName);
1309+
QPixmap pxOriginal;
1310+
auto pixmap = BitmapFactory().pixmap(iconName, false, PyObject_IsTrue(original) ? &pxOriginal : nullptr);
12991311
if(!pixmap.isNull())
1300-
return Py::new_reference_to(wrap.fromQIcon(new QIcon(pixmap)));
1312+
return Py::new_reference_to(wrap.fromQIcon(new QIcon(pxOriginal.isNull()?pixmap:pxOriginal)));
1313+
Py_Return;
1314+
}
1315+
1316+
PyObject* Application::sGetIconContext(PyObject * /*self*/, PyObject *args)
1317+
{
1318+
char *iconName;
1319+
if (!PyArg_ParseTuple(args, "s", &iconName))
1320+
return NULL;
1321+
1322+
Py::List res;
1323+
for (auto &ctx : BitmapFactory().getContext(iconName))
1324+
res.append(Py::String(ctx));
1325+
return Py::new_reference_to(res);
1326+
}
1327+
1328+
PyObject* Application::sAddIconContext(PyObject * /*self*/, PyObject *args)
1329+
{
1330+
char *iconName;
1331+
char *ctx;
1332+
if (!PyArg_ParseTuple(args, "ss", &iconName, &ctx))
1333+
return NULL;
1334+
1335+
BitmapFactory().addContext(iconName, ctx);
13011336
Py_Return;
13021337
}
13031338

@@ -1311,6 +1346,17 @@ PyObject* Application::sIsIconCached(PyObject * /*self*/, PyObject *args)
13111346
return Py::new_reference_to(Py::Boolean(BitmapFactory().findPixmapInCache(iconName, icon)));
13121347
}
13131348

1349+
PyObject* Application::sGetIconNames(PyObject * /*self*/, PyObject *args)
1350+
{
1351+
if (!PyArg_ParseTuple(args, ""))
1352+
return NULL;
1353+
1354+
Py::List res;
1355+
for (auto &name : BitmapFactory().pixmapNames())
1356+
res.append(Py::String(name.toUtf8().constData()));
1357+
return Py::new_reference_to(res);
1358+
}
1359+
13141360
PyObject* Application::sAddCommand(PyObject * /*self*/, PyObject *args)
13151361
{
13161362
char* pName;

0 commit comments

Comments
 (0)