327 lines
10 KiB
C++
327 lines
10 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/****************************************************************************
|
|
* *
|
|
* Copyright (c) 2024 Kacper Donat <kacper@kadet.net> *
|
|
* *
|
|
* This file is part of FreeCAD. *
|
|
* *
|
|
* FreeCAD is free software: you can redistribute it and/or modify it *
|
|
* under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation, either version 2.1 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with FreeCAD. If not, see *
|
|
* <https://www.gnu.org/licenses/>. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#ifndef GUI_INPUTHINT_H
|
|
#define GUI_INPUTHINT_H
|
|
|
|
#include "FCGlobal.h"
|
|
|
|
#include <Qt>
|
|
#include <QString>
|
|
|
|
#include <list>
|
|
#include <vector>
|
|
|
|
namespace Gui
|
|
{
|
|
|
|
/**
|
|
* @brief Representation of one input hint.
|
|
*
|
|
* Each input is essentially a short message defining action with reference to inputs that will
|
|
* trigger that action.
|
|
*
|
|
* For example,
|
|
* @code{c++}
|
|
* using enum InputHint::UserInput;
|
|
*
|
|
* // simple example of hint
|
|
* InputHint selectPointHint {
|
|
* .message = QWidget::tr("%1 select point"),
|
|
* .sequences = { MouseLeft }
|
|
* }
|
|
*
|
|
* // multiple sequences are also allowed in one hint
|
|
* InputHint lockAxisHint {
|
|
* .message = QWidget::tr("%1, %2 or %3 lock X, Y or Z axis"),
|
|
* .sequences = { KeyX, KeyY, KeyZ }
|
|
* }
|
|
*
|
|
* // hints can also use sequences consisting of multiple keys
|
|
* InputHint exitProgramHint {
|
|
* .message = QWidget::tr("%1 exit program"),
|
|
* .sequences = { {KeyAlt, KeyF4} }
|
|
* }
|
|
* @endcode
|
|
*/
|
|
struct InputHint
|
|
{
|
|
enum class UserInput
|
|
{
|
|
// Modifier
|
|
ModifierShift = Qt::KeyboardModifier::ShiftModifier,
|
|
ModifierCtrl = Qt::KeyboardModifier::ControlModifier,
|
|
ModifierAlt = Qt::KeyboardModifier::AltModifier,
|
|
ModifierMeta = Qt::KeyboardModifier::MetaModifier,
|
|
|
|
// Keyboard Keys
|
|
KeySpace = Qt::Key_Space,
|
|
KeyExclam = Qt::Key_Exclam,
|
|
KeyQuoteDbl = Qt::Key_QuoteDbl,
|
|
KeyNumberSign = Qt::Key_NumberSign,
|
|
KeyDollar = Qt::Key_Dollar,
|
|
KeyPercent = Qt::Key_Percent,
|
|
KeyAmpersand = Qt::Key_Ampersand,
|
|
KeyApostrophe = Qt::Key_Apostrophe,
|
|
KeyParenLeft = Qt::Key_ParenLeft,
|
|
KeyParenRight = Qt::Key_ParenRight,
|
|
KeyAsterisk = Qt::Key_Asterisk,
|
|
KeyPlus = Qt::Key_Plus,
|
|
KeyComma = Qt::Key_Comma,
|
|
KeyMinus = Qt::Key_Minus,
|
|
KeyPeriod = Qt::Key_Period,
|
|
KeySlash = Qt::Key_Slash,
|
|
Key0 = Qt::Key_0,
|
|
Key1 = Qt::Key_1,
|
|
Key2 = Qt::Key_2,
|
|
Key3 = Qt::Key_3,
|
|
Key4 = Qt::Key_4,
|
|
Key5 = Qt::Key_5,
|
|
Key6 = Qt::Key_6,
|
|
Key7 = Qt::Key_7,
|
|
Key8 = Qt::Key_8,
|
|
Key9 = Qt::Key_9,
|
|
KeyColon = Qt::Key_Colon,
|
|
KeySemicolon = Qt::Key_Semicolon,
|
|
KeyLess = Qt::Key_Less,
|
|
KeyEqual = Qt::Key_Equal,
|
|
KeyGreater = Qt::Key_Greater,
|
|
KeyQuestion = Qt::Key_Question,
|
|
KeyAt = Qt::Key_At,
|
|
KeyA = Qt::Key_A,
|
|
KeyB = Qt::Key_B,
|
|
KeyC = Qt::Key_C,
|
|
KeyD = Qt::Key_D,
|
|
KeyE = Qt::Key_E,
|
|
KeyF = Qt::Key_F,
|
|
KeyG = Qt::Key_G,
|
|
KeyH = Qt::Key_H,
|
|
KeyI = Qt::Key_I,
|
|
KeyJ = Qt::Key_J,
|
|
KeyK = Qt::Key_K,
|
|
KeyL = Qt::Key_L,
|
|
KeyM = Qt::Key_M,
|
|
KeyN = Qt::Key_N,
|
|
KeyO = Qt::Key_O,
|
|
KeyP = Qt::Key_P,
|
|
KeyQ = Qt::Key_Q,
|
|
KeyR = Qt::Key_R,
|
|
KeyS = Qt::Key_S,
|
|
KeyT = Qt::Key_T,
|
|
KeyU = Qt::Key_U,
|
|
KeyV = Qt::Key_V,
|
|
KeyW = Qt::Key_W,
|
|
KeyX = Qt::Key_X,
|
|
KeyY = Qt::Key_Y,
|
|
KeyZ = Qt::Key_Z,
|
|
KeyBracketLeft = Qt::Key_BracketLeft,
|
|
KeyBackslash = Qt::Key_Backslash,
|
|
KeyBracketRight = Qt::Key_BracketRight,
|
|
KeyAsciiCircum = Qt::Key_AsciiCircum,
|
|
KeyUnderscore = Qt::Key_Underscore,
|
|
KeyQuoteLeft = Qt::Key_QuoteLeft,
|
|
KeyBraceLeft = Qt::Key_BraceLeft,
|
|
KeyBar = Qt::Key_Bar,
|
|
KeyBraceRight = Qt::Key_BraceRight,
|
|
KeyAsciiTilde = Qt::Key_AsciiTilde,
|
|
|
|
// misc keys
|
|
KeyEscape = Qt::Key_Escape,
|
|
KeyTab = Qt::Key_Tab,
|
|
KeyBacktab = Qt::Key_Backtab,
|
|
KeyBackspace = Qt::Key_Backspace,
|
|
KeyReturn = Qt::Key_Return,
|
|
KeyEnter = Qt::Key_Enter,
|
|
KeyInsert = Qt::Key_Insert,
|
|
KeyDelete = Qt::Key_Delete,
|
|
KeyPause = Qt::Key_Pause,
|
|
KeyPrintScr = Qt::Key_Print,
|
|
KeySysReq = Qt::Key_SysReq,
|
|
KeyClear = Qt::Key_Clear,
|
|
|
|
// cursor movement
|
|
KeyHome = Qt::Key_Home,
|
|
KeyEnd = Qt::Key_End,
|
|
KeyLeft = Qt::Key_Left,
|
|
KeyUp = Qt::Key_Up,
|
|
KeyRight = Qt::Key_Right,
|
|
KeyDown = Qt::Key_Down,
|
|
KeyPageUp = Qt::Key_PageUp,
|
|
KeyPageDown = Qt::Key_PageDown,
|
|
|
|
// modifiers
|
|
KeyShift = Qt::Key_Shift,
|
|
KeyControl = Qt::Key_Control,
|
|
KeyMeta = Qt::Key_Meta,
|
|
KeyAlt = Qt::Key_Alt,
|
|
KeyCapsLock = Qt::Key_CapsLock,
|
|
KeyNumLock = Qt::Key_NumLock,
|
|
KeyScrollLock = Qt::Key_ScrollLock,
|
|
|
|
// function keys
|
|
KeyF1 = Qt::Key_F1,
|
|
KeyF2 = Qt::Key_F2,
|
|
KeyF3 = Qt::Key_F3,
|
|
KeyF4 = Qt::Key_F4,
|
|
KeyF5 = Qt::Key_F5,
|
|
KeyF6 = Qt::Key_F6,
|
|
KeyF7 = Qt::Key_F7,
|
|
KeyF8 = Qt::Key_F8,
|
|
KeyF9 = Qt::Key_F9,
|
|
KeyF10 = Qt::Key_F10,
|
|
KeyF11 = Qt::Key_F11,
|
|
KeyF12 = Qt::Key_F12,
|
|
KeyF13 = Qt::Key_F13,
|
|
KeyF14 = Qt::Key_F14,
|
|
KeyF15 = Qt::Key_F15,
|
|
KeyF16 = Qt::Key_F16,
|
|
KeyF17 = Qt::Key_F17,
|
|
KeyF18 = Qt::Key_F18,
|
|
KeyF19 = Qt::Key_F19,
|
|
KeyF20 = Qt::Key_F20,
|
|
KeyF21 = Qt::Key_F21,
|
|
KeyF22 = Qt::Key_F22,
|
|
KeyF23 = Qt::Key_F23,
|
|
KeyF24 = Qt::Key_F24,
|
|
KeyF25 = Qt::Key_F25,
|
|
KeyF26 = Qt::Key_F26,
|
|
KeyF27 = Qt::Key_F27,
|
|
KeyF28 = Qt::Key_F28,
|
|
KeyF29 = Qt::Key_F29,
|
|
KeyF30 = Qt::Key_F30,
|
|
KeyF31 = Qt::Key_F31,
|
|
KeyF32 = Qt::Key_F32,
|
|
KeyF33 = Qt::Key_F33,
|
|
KeyF34 = Qt::Key_F34,
|
|
KeyF35 = Qt::Key_F35,
|
|
|
|
// numpad keys
|
|
KeyNum0 = static_cast<int>(Qt::Key_0) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum1 = static_cast<int>(Qt::Key_1) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum2 = static_cast<int>(Qt::Key_2) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum3 = static_cast<int>(Qt::Key_3) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum4 = static_cast<int>(Qt::Key_4) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum5 = static_cast<int>(Qt::Key_5) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum6 = static_cast<int>(Qt::Key_6) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum7 = static_cast<int>(Qt::Key_7) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum8 = static_cast<int>(Qt::Key_8) | static_cast<int>(Qt::KeypadModifier),
|
|
KeyNum9 = static_cast<int>(Qt::Key_9) | static_cast<int>(Qt::KeypadModifier),
|
|
|
|
// Mouse Keys
|
|
MouseMove = 1 << 16,
|
|
MouseLeft = 2 << 16,
|
|
MouseRight = 3 << 16,
|
|
MouseMiddle = 4 << 16,
|
|
MouseScroll = 5 << 16,
|
|
MouseScrollUp = 6 << 16,
|
|
MouseScrollDown = 7 << 16,
|
|
};
|
|
|
|
struct InputSequence
|
|
{
|
|
std::list<UserInput> keys {};
|
|
|
|
// This is intentionally left as an implicit conversion. For the intended use one UserInput
|
|
// is de facto InputSequence of length one. Therefore, there is no need to make that explicit.
|
|
explicit(false) InputSequence(UserInput key)
|
|
: InputSequence({key})
|
|
{}
|
|
|
|
explicit InputSequence(std::list<UserInput> keys)
|
|
: keys(std::move(keys))
|
|
{}
|
|
|
|
InputSequence(const std::initializer_list<UserInput> keys)
|
|
: keys(keys)
|
|
{}
|
|
|
|
friend bool operator==(const InputSequence& lhs, const InputSequence& rhs)
|
|
{
|
|
return lhs.keys == rhs.keys;
|
|
}
|
|
|
|
friend bool operator!=(const InputSequence& lhs, const InputSequence& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Message associated with the hint.
|
|
*
|
|
* The message should confirm to rules stated in the documentation of the InputHint class.
|
|
* Message can contain placeholders like %1, %2 etc. that will then be replaced with graphical
|
|
* key representation from sequences field.
|
|
*
|
|
* @see https://freecad.github.io/DevelopersHandbook/designguide/input-hints.html
|
|
*/
|
|
QString message;
|
|
|
|
/**
|
|
* @brief List of sequences to be substituted.
|
|
*/
|
|
std::list<InputSequence> sequences;
|
|
|
|
friend bool operator==(const InputHint& lhs, const InputHint& rhs)
|
|
{
|
|
return lhs.message == rhs.message && lhs.sequences == rhs.sequences;
|
|
}
|
|
|
|
friend bool operator!=(const InputHint& lhs, const InputHint& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct StateHints
|
|
{
|
|
T state;
|
|
std::list<InputHint> hints;
|
|
};
|
|
|
|
template<typename T>
|
|
using HintTable = std::vector<StateHints<T>>;
|
|
|
|
template<typename T>
|
|
static std::list<InputHint> lookupHints(
|
|
T state,
|
|
HintTable<T> table,
|
|
const std::list<InputHint>& fallback = {}
|
|
)
|
|
{
|
|
const auto stateMatches = [&state](const StateHints<T>& entry) {
|
|
return entry.state == state;
|
|
};
|
|
|
|
if (auto it = std::ranges::find_if(table, stateMatches); it != table.end()) {
|
|
return it->hints;
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
|
|
} // namespace Gui
|
|
|
|
#endif // GUI_INPUTHINT_H
|