Skip to content

Commit

Permalink
Sketcher/Gui: Extend toolbar framework and fFix toolbar visibility no…
Browse files Browse the repository at this point in the history
…t preserved

=================================================================================

fixes FreeCAD#9208

Essentially:
- The regular mechanism to save toolbar state when changing from one WB to another
is not designed to support changes within one WB (e.g. from edit mode and back).
- At creation time, toolbars can be initialised with default visible or default hidden
state. Additionally, there is third configuration "Unavailable", which refers to a
toolbar that is hidden, and the control to enable it is also hidden by default.
- The ToolBarManager is extended to enable to set the State of one or more toolbars.
- The State refers to changes to be effected on one or more toolbars by client code:
* ForceHidden allows to hide a toolbar and also hide its control (a toolbar not available
in a mode).
* ForceAvailable allows to make a toolbar available by making its control visible, the
toolbar itself is visible or not depending on user settings.
* RestoreDefault allows to bring the control visibility to the default of the toolbar, the
toolbar itself is visible or not depending on user settings.
* SaveState allows to store the current visibility state of a toolbar. It enables client
code to save the state when appropriate. It provides the only option for default "Unavailable"
toolbars, which are fully managed by client code. It provides additional flexibility to save
other toolbar visibility on request.

For the Sketcher this means:
- That edit mode toolbars are not shown outside edit mode.
- That edit mode toolbars and non-edit mode toolbars can be configured independently.
- that edit mode toolbars' state is saved when leaving edit mode if and only if, the
workbench that is selected when leaving edit mode is the Sketcher WB.
- it won't save the state if the user manually selected another WB and then left edit
mode (why? see limitation above).

Limitation:
- When switching to another WB while in edit mode, the other WB is activated before the
current WB (sketcher WB) is deactivated. This means that at sketcher level, the sketcher
has no chance to save states or do other tidy up actions before the tools of the other WB
are activated.
- This, however, is understood as not relevant enough as to warrant changing the mechanisms
in place.
  • Loading branch information
abdullahtahiriyo committed May 29, 2023
1 parent 4a90f6e commit 53c2b3d
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 58 deletions.
118 changes: 97 additions & 21 deletions src/Gui/ToolBarManager.cpp
Expand Up @@ -37,11 +37,11 @@

using namespace Gui;

ToolBarItem::ToolBarItem() : visibility(HideStyle::VISIBLE)
ToolBarItem::ToolBarItem() : visibilityPolicy(DefaultVisibility::Visible)
{
}

ToolBarItem::ToolBarItem(ToolBarItem* item, HideStyle visibility) : visibility(visibility)
ToolBarItem::ToolBarItem(ToolBarItem* item, DefaultVisibility visibilityPolicy) : visibilityPolicy(visibilityPolicy)
{
if (item) {
item->appendItem(this);
Expand Down Expand Up @@ -248,11 +248,31 @@ void ToolBarManager::setup(ToolBarItem* toolBarItems)
toolbars.removeAt(index);
}

bool visible = hPref->GetBool(toolbarName.c_str(), (*it)->visibility == ToolBarItem::HideStyle::VISIBLE);
visible &= (*it)->visibility != ToolBarItem::HideStyle::FORCE_HIDE;
bool visible = false;

// If visibility policy is custom, the toolbar is initialised as not visible, and the
// toggleViewAction to control its visibility is not visible either.
//
// Both are managed under the responsibility of the client code
if((*it)->visibilityPolicy != ToolBarItem::DefaultVisibility::Unavailable) {
bool defaultvisibility = (*it)->visibilityPolicy == ToolBarItem::DefaultVisibility::Visible;

visible = hPref->GetBool(toolbarName.c_str(), defaultvisibility);

// Enable automatic handling of visibility via, for example, (contextual) menu
toolbar->toggleViewAction()->setVisible(true);
}
else { // ToolBarItem::DefaultVisibility::Unavailable
// Prevent that the action to show/hide a toolbar appears on the (contextual) menus.
// This is also managed by the client code for a toolbar with custom policy
toolbar->toggleViewAction()->setVisible(false);
}

// Initialise toolbar item visibility
toolbar->setVisible(visible);
toolbar->toggleViewAction()->setVisible((*it)->visibility != ToolBarItem::HideStyle::FORCE_HIDE);
toolbar->toggleViewAction()->setProperty("HideStyle", static_cast<int>((*it)->visibility));

// Store item visibility policy within the action
toolbar->toggleViewAction()->setProperty("DefaultVisibility", static_cast<int>((*it)->visibilityPolicy));

// setup the toolbar
setup(*it, toolbar);
Expand Down Expand Up @@ -350,15 +370,15 @@ void ToolBarManager::saveState() const
return true;
}

QVariant property = action->property("HideStyle");
QVariant property = action->property("DefaultVisibility");
if (property.isNull()) {
return false;
}

// If hide style is FORCE_HIDE then never save the state because it's
// always controlled by the application.
auto value = static_cast<ToolBarItem::HideStyle>(property.toInt());
return value == ToolBarItem::HideStyle::FORCE_HIDE;
// If DefaultVisibility is Unavailable then never save the state because it's
// always controlled by the client code.
auto value = static_cast<ToolBarItem::DefaultVisibility>(property.toInt());
return value == ToolBarItem::DefaultVisibility::Unavailable;
};

ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")
Expand Down Expand Up @@ -450,26 +470,82 @@ QList<QToolBar*> ToolBarManager::toolBars() const
return tb;
}

void ToolBarManager::setToolbarVisibility(bool show, const QList<QString>& names) {
for (auto& name : names) {
setToolbarVisibility(show, name);
ToolBarItem::DefaultVisibility ToolBarManager::getToolbarPolicy(const QToolBar* toolbar) const
{
auto* action = toolbar->toggleViewAction();

QVariant property = action->property("DefaultVisibility");
if (property.isNull()) {
return ToolBarItem::DefaultVisibility::Visible;
}

return static_cast<ToolBarItem::DefaultVisibility>(property.toInt());
}

void ToolBarManager::setToolbarVisibility(bool show, const QString& name) {
void ToolBarManager::setState(const QList<QString>& names, State state)
{
for (auto& name : names) {
setState(name, state);
}
}

void ToolBarManager::setState(const QString& name, State state)
{
ParameterGrp::handle hPref = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("MainWindow")->GetGroup("Toolbars");

auto visibility = [hPref, name](bool defaultvalue) {
return hPref->GetBool(name.toStdString().c_str(), defaultvalue);
};

auto saveVisibility = [hPref, name](bool value) {
hPref->SetBool(name.toStdString().c_str(), value);
};

auto showhide = [visibility](QToolBar* toolbar, ToolBarItem::DefaultVisibility policy) {

auto show = visibility( policy == ToolBarItem::DefaultVisibility::Visible );

if(show) {
toolbar->show();
}
else {
toolbar->hide();
}
};

QToolBar* tb = findToolBar(toolBars(), name);
if (tb) {
if (show) {
if(hPref->GetBool(name.toStdString().c_str(), true))
tb->show();

if (state == State::RestoreDefault) {

auto policy = getToolbarPolicy(tb);

if(policy == ToolBarItem::DefaultVisibility::Unavailable) {
tb->hide();
tb->toggleViewAction()->setVisible(false);
}
else {
tb->toggleViewAction()->setVisible(true);

showhide(tb, policy);
}
}
else if (state == State::ForceAvailable) {

auto policy = getToolbarPolicy(tb);

tb->toggleViewAction()->setVisible(true);

showhide(tb, policy);
}
else {
tb->hide();
tb->toggleViewAction()->setVisible(false);
else if (state == State::ForceHidden) {
tb->toggleViewAction()->setVisible(false); // not visible in context menus
tb->hide(); // toolbar not visible

}
else if (state == State::SaveState) {
auto show = tb->isVisible();
saveVisibility(show);
}
}
}
33 changes: 25 additions & 8 deletions src/Gui/ToolBarManager.h
Expand Up @@ -36,14 +36,19 @@ namespace Gui {
class GuiExport ToolBarItem
{
public:
enum class HideStyle {
VISIBLE,
HIDDEN, // toolbar hidden by default
FORCE_HIDE // Force a toolbar to be hidden. For when all elements are disabled at some point in a workbench.
/** Manages the default visibility status of a toolbar item, as well as the default status
* of the toggleViewAction usable by the contextual menu to enable and disable its visibility
*/
enum class DefaultVisibility {
Visible, // toolbar is hidden by default, visibility toggle action is enabled
Hidden, // toolbar hidden by default, visibility toggle action is enabled
Unavailable, // toolbar visibility is managed independently by client code and defaults to
// hidden, visibility toggle action is disabled by default (it is unavailable
// to the UI).
};

ToolBarItem();
explicit ToolBarItem(ToolBarItem* item, HideStyle visibility = HideStyle::VISIBLE);
explicit ToolBarItem(ToolBarItem* item, DefaultVisibility visibilityPolicy = DefaultVisibility::Visible);
~ToolBarItem();

void setCommand(const std::string&);
Expand All @@ -63,7 +68,7 @@ class GuiExport ToolBarItem
ToolBarItem& operator << (const std::string& command);
QList<ToolBarItem*> getItems() const;

HideStyle visibility;
DefaultVisibility visibilityPolicy;

private:
std::string _name;
Expand All @@ -80,6 +85,14 @@ class GuiExport ToolBarItem
class GuiExport ToolBarManager
{
public:

enum class State {
ForceHidden, // Forces a toolbar to hide and hides the toggle action
ForceAvailable, // Forces a toolbar toggle action to show, visibility depends on user config
RestoreDefault, // Restores a toolbar toggle action default, visibility as user config
SaveState, // Saves the state of the toolbars
};

/// The one and only instance.
static ToolBarManager* getInstance();
static void destruct();
Expand All @@ -90,11 +103,15 @@ class GuiExport ToolBarManager
void retranslate() const;

void setMovable(bool movable) const;
void setToolbarVisibility(bool show, const QList<QString>& names);
void setToolbarVisibility(bool show, const QString& name);

void setState(const QList<QString>& names, State state);
void setState (const QString& name, State state);

protected:
void setup(ToolBarItem*, QToolBar*) const;

ToolBarItem::DefaultVisibility getToolbarPolicy(const QToolBar *) const;

/** Returns a list of all currently existing toolbars. */
QList<QToolBar*> toolBars() const;
QToolBar* findToolBar(const QList<QToolBar*>&, const QString&) const;
Expand Down
2 changes: 1 addition & 1 deletion src/Gui/Workbench.cpp
Expand Up @@ -774,7 +774,7 @@ ToolBarItem* StdWorkbench::setupToolBars() const
<< "Separator" << "Std_Refresh";

// Clipboard
auto clipboard = new ToolBarItem( root , ToolBarItem::HideStyle::HIDDEN );
auto clipboard = new ToolBarItem( root , ToolBarItem::DefaultVisibility::Hidden );
clipboard->setCommand("Clipboard");
*clipboard << "Std_Cut" << "Std_Copy" << "Std_Paste";

Expand Down
90 changes: 62 additions & 28 deletions src/Mod/Sketcher/Gui/Workbench.cpp
Expand Up @@ -24,8 +24,10 @@

#include "Utils.h"
#include "Workbench.h"
#include <Base/Console.h>
#include <Gui/Application.h>
#include <Gui/Document.h>
#include <Gui/WorkbenchManager.h>
#include <Mod/Sketcher/App/Constraint.h>

using namespace SketcherGui;
Expand Down Expand Up @@ -106,34 +108,37 @@ Gui::ToolBarItem* Workbench::setupToolBars() const
addSketcherWorkbenchSketchActions(*sketcher);

Gui::ToolBarItem* sketcherEditMode =
new Gui::ToolBarItem(root, Gui::ToolBarItem::HideStyle::FORCE_HIDE);
new Gui::ToolBarItem(root, Gui::ToolBarItem::DefaultVisibility::Unavailable);
sketcherEditMode->setCommand("Sketcher Edit Mode");
addSketcherWorkbenchSketchEditModeActions(*sketcherEditMode);

Gui::ToolBarItem* geom = new Gui::ToolBarItem(root, Gui::ToolBarItem::HideStyle::FORCE_HIDE);
Gui::ToolBarItem* geom =
new Gui::ToolBarItem(root, Gui::ToolBarItem::DefaultVisibility::Unavailable);
geom->setCommand("Sketcher geometries");
addSketcherWorkbenchGeometries(*geom);

Gui::ToolBarItem* cons = new Gui::ToolBarItem(root, Gui::ToolBarItem::HideStyle::FORCE_HIDE);
Gui::ToolBarItem* cons =
new Gui::ToolBarItem(root, Gui::ToolBarItem::DefaultVisibility::Unavailable);
cons->setCommand("Sketcher constraints");
addSketcherWorkbenchConstraints(*cons);

Gui::ToolBarItem* consaccel =
new Gui::ToolBarItem(root, Gui::ToolBarItem::HideStyle::FORCE_HIDE);
new Gui::ToolBarItem(root, Gui::ToolBarItem::DefaultVisibility::Unavailable);
consaccel->setCommand("Sketcher tools");
addSketcherWorkbenchTools(*consaccel);

Gui::ToolBarItem* bspline = new Gui::ToolBarItem(root, Gui::ToolBarItem::HideStyle::FORCE_HIDE);
Gui::ToolBarItem* bspline =
new Gui::ToolBarItem(root, Gui::ToolBarItem::DefaultVisibility::Unavailable);
bspline->setCommand("Sketcher B-spline tools");
addSketcherWorkbenchBSplines(*bspline);

Gui::ToolBarItem* virtualspace =
new Gui::ToolBarItem(root, Gui::ToolBarItem::HideStyle::FORCE_HIDE);
new Gui::ToolBarItem(root, Gui::ToolBarItem::DefaultVisibility::Unavailable);
virtualspace->setCommand("Sketcher virtual space");
addSketcherWorkbenchVirtualSpace(*virtualspace);

Gui::ToolBarItem* edittools =
new Gui::ToolBarItem(root, Gui::ToolBarItem::HideStyle::FORCE_HIDE);
new Gui::ToolBarItem(root, Gui::ToolBarItem::DefaultVisibility::Unavailable);
edittools->setCommand("Sketcher edit tools");
addSketcherWorkbenchEditTools(*edittools);

Expand All @@ -147,13 +152,6 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const
return root;
}

void Workbench::activated()
{
Gui::Document* doc = Gui::Application::Instance->activeDocument();
if (isSketchInEdit(doc)) {
enterEditMode();
}
}

namespace
{
Expand All @@ -174,28 +172,64 @@ inline const QStringList nonEditModeToolbarNames()
}
}// namespace

void Workbench::activated()
{
/* When the workbench is activated, it may happen that we are in edit mode or not.
* If we are not in edit mode, the function enterEditMode (called by the ViewProvider) takes
* care to save the state of toolbars outside of edit mode. We cannot do it here, as we are
* coming from another WB.
*
* If we moved to another WB from edit mode, the new WB was activated before deactivating this.
* Therefore we had no chance to tidy up the save state. We assume a loss of any CHANGE to
* toolbar configuration since last entering edit mode in this case (for any change in
* configuration to be stored, the edit mode must be left while the selected Workbench is the
* sketcher WB).
*
* However, now that we are back (from another WB), we need to make the toolbars available.
* These correspond to the last saved state.
*/
Base::Console().Log("Sketch WB: Activated\n");
Gui::Document* doc = Gui::Application::Instance->activeDocument();
if (isSketchInEdit(doc)) {
Gui::ToolBarManager::getInstance()->setState(editModeToolbarNames(),
Gui::ToolBarManager::State::ForceAvailable);
}
}

void Workbench::enterEditMode()
{
/*Modify toolbars dynamically.
First save state of toolbars in case user changed visibility of a toolbar but he's not changing
the wb. This happens in someone works directly from sketcher, changing from edit mode to
not-edit-mode*/
Gui::ToolBarManager::getInstance()->saveState();
/* Ensure the state left by the non-edit mode toolbars is saved (in case of changing to edit
* mode) without changing workbench
*/
Gui::ToolBarManager::getInstance()->setState(nonEditModeToolbarNames(),
Gui::ToolBarManager::State::SaveState);

Gui::ToolBarManager::getInstance()->setToolbarVisibility(true, editModeToolbarNames());
Gui::ToolBarManager::getInstance()->setToolbarVisibility(false, nonEditModeToolbarNames());
Gui::ToolBarManager::getInstance()->setState(editModeToolbarNames(),
Gui::ToolBarManager::State::ForceAvailable);
Gui::ToolBarManager::getInstance()->setState(nonEditModeToolbarNames(),
Gui::ToolBarManager::State::ForceHidden);
}

void Workbench::leaveEditMode()
{
/*Modify toolbars dynamically.
First save state of toolbars in case user changed visibility of a toolbar but he's not changing
the wb. This happens in someone works directly from sketcher, changing from edit mode to
not-edit-mode*/
Gui::ToolBarManager::getInstance()->saveState();
/* Ensure the state left by the edit mode toolbars is saved (in case of changing to edit mode)
* without changing workbench.
*
* However, do not save state if the current workbench is not the Sketcher WB, because otherwise
* we would be saving the state of the currently activated workbench, and the toolbars would
* disappear (as the toolbars of that other WB are only visible).
*/
auto* workbench = Gui::WorkbenchManager::instance()->active();

if (workbench->name() == "SketcherWorkbench") {
Gui::ToolBarManager::getInstance()->setState(editModeToolbarNames(),
Gui::ToolBarManager::State::SaveState);
}

Gui::ToolBarManager::getInstance()->setToolbarVisibility(false, editModeToolbarNames());
Gui::ToolBarManager::getInstance()->setToolbarVisibility(true, nonEditModeToolbarNames());
Gui::ToolBarManager::getInstance()->setState(editModeToolbarNames(),
Gui::ToolBarManager::State::RestoreDefault);
Gui::ToolBarManager::getInstance()->setState(nonEditModeToolbarNames(),
Gui::ToolBarManager::State::RestoreDefault);
}

namespace SketcherGui
Expand Down

0 comments on commit 53c2b3d

Please sign in to comment.