Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a162b56
[render] add from/toString helpers for input pair types
alemuntoni Aug 19, 2026
d72e4d8
[base] add StringConvertible concept
alemuntoni Aug 19, 2026
038d78c
[render] add new InputActionMap
alemuntoni Aug 19, 2026
884c4ed
[render] from/toString for TrackballMotionType, and new file for Abst…
alemuntoni Aug 19, 2026
4ede241
[render] using InputActionMap on TrackballSettings
alemuntoni Aug 19, 2026
87c35ec
[render] SelectionEditorSettings use InputActionMap
alemuntoni Aug 19, 2026
1ba699f
[render] MeshSelector uses InputActionMap
alemuntoni Aug 19, 2026
4bf08ca
[render] add MeshSelectorEditorSettings struct
alemuntoni Aug 19, 2026
92b28f0
[render] move embedded actions to global actions in AbstractViewerDrawer
alemuntoni Aug 19, 2026
7f5a59a
[render] expose action maps from settings classes
alemuntoni Aug 19, 2026
bdff660
[render] Shortcuts tab and infrastructure for SettingsDialog
alemuntoni Aug 19, 2026
2e33c2b
[render] infrastructure for ActionMap extraction from AbstractViewerD…
alemuntoni Aug 20, 2026
8722625
Merge branch 'main' into save_settings
alemuntoni Aug 20, 2026
31140cb
[render] cleanups and docs
alemuntoni Aug 20, 2026
6df108a
[render] populate key bindings on SettingsDialog
alemuntoni Aug 20, 2026
7234cd3
[render] infrastructure for input filtering in AbstractInputActionMap
alemuntoni Aug 20, 2026
5b2e32d
[render] possibility to edit shortcuts on SettingsDialog
alemuntoni Aug 20, 2026
2c036e5
[render] introduce Mouse Atomic Actions (e.g. focus) with proper mana…
alemuntoni Aug 20, 2026
2486f58
[render] allow to serialize/deserialize custom shortcuts on config file
alemuntoni Aug 20, 2026
29bded4
[render] possibility to disable action
alemuntoni Aug 20, 2026
088d578
[render] editors support double click actions
alemuntoni Aug 20, 2026
c94205b
[render] SettingsDialog warning when two shortcuts collide
alemuntoni Aug 20, 2026
89f9a86
[render] bugfixes and docs
alemuntoni Aug 21, 2026
3492dff
[render] support multiple shortcuts per action
alemuntoni Aug 21, 2026
d9295fd
[render] move InputBindingsWidget
alemuntoni Aug 21, 2026
5a141ba
[render] docs and fixes
alemuntoni Aug 21, 2026
82c78cc
[render] fix drag event distinction between single and double click
alemuntoni Aug 21, 2026
5d89f40
Merge branch 'main' into save_settings
alemuntoni Aug 21, 2026
45572bd
Merge branch 'main' into save_settings
alemuntoni Sep 1, 2026
941913f
Merge branch 'main' into save_settings
alemuntoni Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions vclib/render/include/vclib/bgfx/drawers/viewer_drawer_bgfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,21 +161,12 @@ class ViewerDrawerBGFX : public AbstractViewerDrawer<DerivedRenderApp>
return block;
}

bool onMouseDoubleClick(
MouseButton::Enum button,
double x,
double y,
const KeyModifiers& modifiers) override
// shadows Base::readDepthRequest: the requested homogeneousNDC is always
// overridden with the value required by the current bgfx backend
void readDepthRequest(double x, double y, bool homogeneousNDC = true)
{
bool block = Base::onMouseDoubleClick(button, x, y, modifiers);

if (!block && button == MouseButton::LEFT) {
const bool homogeneousNDC =
Context::instance().capabilites().homogeneousDepth;

Base::readDepthRequest(x, y, homogeneousNDC);
}
return block;
homogeneousNDC = Context::instance().capabilites().homogeneousDepth;
Base::readDepthRequest(x, y, homogeneousNDC);
}

private:
Expand Down
57 changes: 39 additions & 18 deletions vclib/render/include/vclib/bgfx/editors/selection_editor_bgfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,25 +337,17 @@ class SelectionEditorBGFX : public Editor<ViewerDrawer>
double y,
const vcl::KeyModifiers& modifiers) override
{
if (!isSelectionActive())
return false;

auto actionOpt = mSettings.mouseBindings.action({button, modifiers});
if (actionOpt.has_value() && !mSelectionInProgress) {
SelectionDragAction action = actionOpt.value();
return mousePress(button, x, y, modifiers, false);
}

if (!mActionCreationPending) {
savePreSelectionStates();
mActionCreationPending = true;
}
mSelectionInProgress = true;
mSelectionAnchor = Point2d {x, y};
mSelectionBox = Box2d({x, y});
mCurrentMouseAction = actionOpt.value();
mCurrentSelectionModes = actionModesForSettings(action);
return true; // Smart blocking
}
return false;
bool onMouseDoubleClick(
vcl::MouseButton::Enum button,
double x,
double y,
const vcl::KeyModifiers& modifiers) override
{
// Treat double-click as a press for selection
return mousePress(button, x, y, modifiers, true);
}

bool onMouseRelease(
Expand Down Expand Up @@ -418,6 +410,35 @@ class SelectionEditorBGFX : public Editor<ViewerDrawer>
return mSettings.selectVertices || mSettings.selectFaces;
}

bool mousePress(
vcl::MouseButton::Enum button,
double x,
double y,
const vcl::KeyModifiers& modifiers,
bool doubleClick)
{
if (!isSelectionActive())
return false;

auto actionOpt =
mSettings.mouseBindings.action({button, modifiers, doubleClick});
if (actionOpt.has_value() && !mSelectionInProgress) {
SelectionDragAction action = actionOpt.value();

if (!mActionCreationPending) {
savePreSelectionStates();
mActionCreationPending = true;
}
mSelectionInProgress = true;
mSelectionAnchor = Point2d {x, y};
mSelectionBox = Box2d({x, y});
mCurrentMouseAction = actionOpt.value();
mCurrentSelectionModes = actionModesForSettings(action);
return true; // Smart blocking
}
return false;
}

std::vector<SelectionMode> actionModesForSettings(
SelectionAtomicAction action) const
{
Expand Down
14 changes: 0 additions & 14 deletions vclib/render/include/vclib/opengl2/drawers/viewer_drawer_opengl2.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,6 @@ class ViewerDrawerOpenGL2 : public AbstractViewerDrawer<DerivedRenderApp>
for (auto& obj : *(ParentViewer::mDrawList))
obj->draw();
}

// events
bool onMouseDoubleClick(
MouseButton::Enum button,
double x,
double y,
const KeyModifiers& modifiers) override
{
bool block = ParentViewer::onMouseDoubleClick(button, x, y, modifiers);
if (!block && button == MouseButton::LEFT) {
ParentViewer::readDepthRequest(x, y);
}
return block;
}
};

} // namespace vcl
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// VCLib - Visual Computing Library
// Copyright (C) 2021-2026 Visual Computing Lab, ISTI - CNR.
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/.

#ifndef VCL_QT_GUI_SETTINGS_DIALOG_INPUT_BINDINGS_WIDGET_H
#define VCL_QT_GUI_SETTINGS_DIALOG_INPUT_BINDINGS_WIDGET_H

#include <QWidget>

#include <functional>
#include <map>
#include <memory>
#include <string>

namespace vcl {
class AbstractInputActionMap;
} // namespace vcl

namespace vcl::qt {

namespace Ui {
class InputBindingsWidget;
} // namespace Ui

/**
* @brief A widget listing all the actions of a single AbstractInputActionMap,
* each with a ShortcutButton to view and reassign its current binding.
*
* Edits made by the user are buffered in mPendingBindings and are only
* written back to the underlying action map when applySettings() is called,
* so that closing the settings dialog without applying discards the changes.
*/
class InputBindingsWidget : public QWidget
{
Q_OBJECT

std::unique_ptr<Ui::InputBindingsWidget> mUI;
std::reference_wrapper<AbstractInputActionMap> mMap;
// actionId -> pending input strings, not yet applied to mMap
std::map<std::string, std::vector<std::string>> mPendingBindings;

public:
struct ActionInfo
{
std::string id;
std::string name;
};

explicit InputBindingsWidget(
std::reference_wrapper<AbstractInputActionMap> map,
QWidget* parent = nullptr);
~InputBindingsWidget() override;

void applySettings();

// Conflict resolution interface
int inputType() const;
std::string mapName() const;

std::vector<ActionInfo> getActions() const;

std::vector<std::string> currentInputs(const std::string& actionId) const;

void setConflict(
const std::string& actionId,
bool hasConflict,
const QString& tooltip = "");

void clearAllConflicts();

signals:
void bindingsChanged();

private:
void populateTable();
};

} // namespace vcl::qt

#endif // VCL_QT_GUI_SETTINGS_DIALOG_INPUT_BINDINGS_WIDGET_H
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
#include <QToolBar>
#include <QWidget>

#include <functional>
#include <vector>

namespace vcl {
class AbstractInputActionMap;
} // namespace vcl

namespace vcl::qt {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// VCLib - Visual Computing Library
// Copyright (C) 2021-2026 Visual Computing Lab, ISTI - CNR.
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/.

#ifndef VCL_QT_SHORTCUTS_SETTINGS_TAB_H
#define VCL_QT_SHORTCUTS_SETTINGS_TAB_H

#include <vclib/qt/gui/settings_dialog/settings_dialog_tab.h>
#include <vclib/render/input/action_map_group.h>

#include <QIcon>
#include <QString>
#include <QWidget>

#include <functional>

namespace vcl::qt {

class InputBindingsWidget;

/**
* @brief The SettingsDialogTab that lets the user browse and customize all
* the input bindings (shortcuts) exposed by the viewer and its active
* editors.
*
* The available ActionMapGroup%s are obtained on demand from mProvider (e.g.
* a callback into the viewer), so the tab always reflects the editors that
* are currently pushed into the viewer.
*/
class ShortcutsSettingsTab : public SettingsDialogTab
{
std::function<std::vector<ActionMapGroup>()> mProvider;
// one InputBindingsWidget per action map, paired with its owning group
// name, used by checkConflicts() to scope conflict detection
std::vector<std::pair<InputBindingsWidget*, std::string>> mWidgets;

public:
explicit ShortcutsSettingsTab(
std::function<std::vector<ActionMapGroup>()> provider) :
mProvider(std::move(provider))
{
}

~ShortcutsSettingsTab() override = default;

QString category() const override;

QString name() const override;

QWidget* createWidget(QWidget* parent) override;

void applySettings() override;

void saveSettings(nlohmann::json& j) const override;

void updateToolbarFrames(QToolBar* /*toolbar*/) override {}

private:
void checkConflicts();
};

} // namespace vcl::qt

#endif // VCL_QT_SHORTCUTS_SETTINGS_TAB_H
61 changes: 61 additions & 0 deletions vclib/render/include/vclib/qt/gui/shortcut_button.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// VCLib - Visual Computing Library
// Copyright (C) 2021-2026 Visual Computing Lab, ISTI - CNR.
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/.

#ifndef VCL_QT_SHORTCUT_BUTTON_H
#define VCL_QT_SHORTCUT_BUTTON_H

#include <vclib/render/input/abstract_input_action_map.h>

#include <QPushButton>
#include <QTimer>

#include <functional>
#include <string>

namespace vcl::qt {

/**
* @brief A push button that, when clicked, listens for the next key or mouse
* input and reports it as a string via the onInputCaptured callback.
*
* Used by InputBindingsWidget to let the user interactively (re)assign a
* shortcut to an action. The kind of input it listens for (key, mouse button
* or scroll axis) is restricted by \p mExpectedType, so that e.g. a button
* editing a mouse binding ignores keyboard events.
*/
class ShortcutButton : public QPushButton
{
AbstractInputActionMap::InputType mExpectedType;
bool mListening = false;
QString mOriginalText;
// used to distinguish a single click from the first click of a double
// click when capturing mouse bindings
QTimer* mDoubleClickTimer = nullptr;
Qt::MouseButton mPendingButton;
Qt::KeyboardModifiers mPendingModifiers;

public:
std::function<void(const std::string&)> onInputCaptured;

explicit ShortcutButton(
AbstractInputActionMap::InputType expectedType,
const QString& text,
QWidget* parent = nullptr);

void startListening();

protected:
void keyPressEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
};

} // namespace vcl::qt

#endif // VCL_QT_SHORTCUT_BUTTON_H
13 changes: 0 additions & 13 deletions vclib/render/include/vclib/qt/mesh_viewer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,6 @@ class MeshViewer;

class ViewerSettingsFrame;

class KeyFilter : public QObject
{
Q_OBJECT

public:
KeyFilter(QObject* parent = nullptr) : QObject(parent) {}

protected:
bool eventFilter(QObject* obj, QEvent* event) override;
};

class MeshViewer : public QMainWindow
{
Q_OBJECT
Expand Down Expand Up @@ -322,8 +311,6 @@ public slots:

void addEditorFrame(QWidget* frame);

void keyPressEvent(QKeyEvent* event) override;

private:
void setupSettingsButton();

Expand Down
Loading
Loading