Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
262 changes: 131 additions & 131 deletions src-ui-wx/app-shell/KeyBindings.cpp

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions src-ui-wx/app-shell/KeyBindingsDialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#pragma once

/***************************************************************
* This source files comes from the xLights project
* https://www.xlights.org
* https://github.com/xLightsSequencer/xLights
* See the github commit history for a record of contributing
* developers.
* Copyright claimed based on commit dates recorded in Github
* License: https://github.com/xLightsSequencer/xLights/blob/master/License.txt
**************************************************************/

#include <functional>

#include <wx/button.h>
#include <wx/dialog.h>
#include <wx/intl.h>
#include <wx/sizer.h>

#include "preferences/KeyBindingsSettingsPanel.h"

class xLightsFrame;

// Standalone, MODELESS Key Bindings editor. Hosts the filterable, category-
// scoped, described KeyBindingsSettingsPanel so the window can stay open while
// you keep working in Layout/Sequencer. Edits apply to the live KeyBindingMap
// immediately; they are persisted (keyBindings.Save(), via the panel's
// TransferDataFromWindow) when the window is closed.
class KeyBindingsDialog : public wxDialog {
public:
KeyBindingsDialog(wxWindow* parent, xLightsFrame* frame, std::function<void()> onClosed)
: wxDialog(parent, wxID_ANY, _("Key Bindings"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
auto* top = new wxBoxSizer(wxVERTICAL);
_panel = new KeyBindingsSettingsPanel(this, frame);
top->Add(_panel, 1, wxEXPAND | wxALL, 5);

// Save commits the staged edits; Cancel (and closing the window)
// discards them - editing is not applied in real time.
auto* btnRow = new wxBoxSizer(wxHORIZONTAL);
btnRow->AddStretchSpacer(1);
auto* cancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
auto* save = new wxButton(this, wxID_SAVE, _("Save"));
btnRow->Add(cancel, 0, wxRIGHT, 8);
btnRow->Add(save, 0);
top->Add(btnRow, 0, wxEXPAND | wxALL, 8);
save->SetDefault();

// Size from the panel's content (list columns + editor + wheel); do not
// hard-code a size, which used to open too small and squash the wheel.
SetSizerAndFit(top);
SetMinSize(GetSize());

Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
if (_panel != nullptr) _panel->CommitChanges();
Close();
}, wxID_SAVE);
Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Close(); }, wxID_CANCEL);
Bind(wxEVT_CLOSE_WINDOW, [this, onClosed](wxCloseEvent&) {
// No implicit save - closing without Save discards the working copy.
if (onClosed) {
onClosed();
}
Destroy();
});
}

private:
KeyBindingsSettingsPanel* _panel = nullptr;
};
970 changes: 970 additions & 0 deletions src-ui-wx/preferences/KeyBindingsSettingsPanel.cpp

Large diffs are not rendered by default.

153 changes: 153 additions & 0 deletions src-ui-wx/preferences/KeyBindingsSettingsPanel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#pragma once

/***************************************************************
* This source file comes from the xLights project
* https://www.xlights.org
* https://github.com/xLightsSequencer/xLights
* See the github commit history for a record of contributing
* developers.
* Copyright claimed based on commit dates recorded in Github
* License: https://github.com/xLightsSequencer/xLights/blob/master/License.txt
**************************************************************/

#include <wx/panel.h>
#include <wx/dataview.h> // wxDataViewListCtrl - native drag-reorder + drop line

#include <string>
#include <vector>

#include "app-shell/KeyBindings.h" // KeyBindingMap held by value (working copy)

class KeyBindingMap;
class KeyBinding;
class EffectManager;
class xLightsFrame;
class wxButton;
class wxChoice;
class wxListCtrl;
class wxSearchCtrl;
class wxListEvent;
class wxCommandEvent;
class wxMouseEvent;

// The Key Bindings editor body, hosted by the modeless KeyBindingsDialog.
// Filterable, category/scope-scoped bindings list on the left; the selected
// binding is edited inline on the right (key captured by keypress + modifier
// checkboxes), with a live Wheel-of-Effects preview for effect bindings.
// Edits are staged on a working copy and only written to the live KeyBindingMap
// by CommitChanges() (the dialog's Save button) - Cancel discards them.
class KeyBindingsSettingsPanel : public wxPanel
{
EffectManager* _effectManager = nullptr;
KeyBindingMap* _keyBindings = nullptr; // points at _working (edits are staged)
KeyBindingMap* _liveKeyBindings = nullptr; // the real map; only touched on Save
KeyBindingMap _working; // edited copy; discarded on Cancel
xLightsFrame* _xLights = nullptr;

void LoadList();
wxString BuildDetails(const KeyBinding& b) const;
void RefreshDuplicateHighlights();
void RefreshRow(int row, const KeyBinding& b);
int GetSelectedKeyBindingIndex() const; // selected row, or -1
long RowBindingId(int row) const; // binding id for a row, or -1
void SelectKey(int id);
void RemoveSelected(); // delete the selected effect binding

// Inline editor (replaces the old modal popup): the selected binding is
// edited directly below the list - shortcut captured by keypress, and the
// effect/preset/setting chosen with a compact control.
KeyBinding* SelectedBinding() const;
void PopulateEditor();
void OnShortcutKey(wxKeyEvent& event);
void ClearShortcut();
void ApplyEffectChoice();

// Reorder the effect bindings (drag with a drop line, or Move Up/Down).
// Only EFFECT-type bindings reorder - they drive the Wheel of Effects.
void OnDvBeginDrag(wxDataViewEvent& event);
void OnDvDropPossible(wxDataViewEvent& event);
void OnDvDrop(wxDataViewEvent& event);
void MoveSelectedEffect(int delta);
long _dragFromId = -1;
wxButton* _moveUpBtn = nullptr;
wxButton* _moveDownBtn = nullptr;

wxStaticText* _editorTitle = nullptr;
wxTextCtrl* _shortcutField = nullptr; // read-only; captures the base key
wxButton* _clearShortcutBtn = nullptr;
wxCheckBox* _cbControl = nullptr; // Command on macOS
wxCheckBox* _cbAlt = nullptr;
wxCheckBox* _cbShift = nullptr;
wxCheckBox* _cbRawControl = nullptr; // physical Control on macOS
wxStaticText* _valueLabel = nullptr;
wxChoice* _effectChoice = nullptr;
wxChoice* _presetChoice = nullptr;
wxTextCtrl* _settingCtrl = nullptr;
void SyncModifierChecks(const KeyBinding& b);
// Which value controls were last shown. Re-laying out on every selection
// change resized the list (and reset the Details column width), so only
// relayout when the editor's shape actually changes.
int _lastEditorShape = -1;

// Live mini Wheel-of-Effects preview: draws each effect at its clock
// position, highlighting the selected one, and repaints on reorder. Only
// shown for the Effects category - it means nothing for other bindings.
wxPanel* _wheelPanel = nullptr;
wxSizer* _wheelBox = nullptr;
void OnPaintWheel(wxPaintEvent& event);
// The bindings the sequencer's Wheel of Effects actually shows: the first
// kWheelSlots enabled, Sequencer-scoped EFFECT bindings, in order (see
// EffectsGrid.cpp). Effects past that cut-off never appear on the wheel.
static constexpr int kWheelSlots = 18;
std::vector<const KeyBinding*> WheelBindings() const;
void UpdateWheelVisibility();
void FitColumns(); // size Details to the leftover space
// Rebuild the Scope dropdown so it only offers scopes that actually occur
// in the currently selected Category (no more empty combinations).
void RebuildScopeChoices();
// Make a just-added binding visible (switch category, clear filter, select).
void RevealBinding(int id, int category);
bool _fitting = false; // re-entrancy guard (SetWidth can re-fire wxEVT_SIZE)

static wxString RenderShortcut(const KeyBinding& b);
static wxString RenderModifiers(const KeyBinding& b); // just the modifier symbols
static wxString RenderKey(const KeyBinding& b); // just the base key

wxChoice* Choice_Category = nullptr; // filters the list by binding kind
wxChoice* Choice_Scope = nullptr;
wxDataViewListCtrl* _dvList = nullptr;
// Hold the columns by pointer: wxDataViewCtrl's positional GetColumn(i)
// shifts once a column is hidden, which silently hit the wrong column.
wxDataViewColumn* _colGrip = nullptr;
wxDataViewColumn* _colAction = nullptr;
wxDataViewColumn* _colPos = nullptr;
wxDataViewColumn* _colDetails = nullptr;
wxDataViewColumn* _colMods = nullptr;
wxDataViewColumn* _colKey = nullptr;
std::vector<long> _rowIds; // row index -> binding id
wxSearchCtrl* _filterCtrl = nullptr;
wxString _filter; // lower-cased; whitespace-tokenised AND match in LoadList

void OnChoice_ScopeSelect(wxCommandEvent& event);
void OnButton_AddEffectClick(wxCommandEvent& event);
void OnButtonAddApplySettingClick(wxCommandEvent& event);
void OnButtonAddPresetClick(wxCommandEvent& event);

// Broad category a binding falls into ("Effects", "Presets",
// "Apply Settings" or "Commands"), used by the Category dropdown filter.
static int CategoryIndexOf(const std::string& type);

public:
// Human-readable name for a binding type (e.g. "Timing: Add").
static wxString FriendlyName(const std::string& type);

KeyBindingsSettingsPanel(wxWindow* parent, xLightsFrame* f, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize);
virtual ~KeyBindingsSettingsPanel();

virtual bool TransferDataFromWindow() override;
virtual bool TransferDataToWindow() override;

// Apply the staged edits to the real key-binding map and persist. Called
// by the dialog's Save button; Cancel/close just discards the working copy.
void CommitChanges();
};
12 changes: 9 additions & 3 deletions src-ui-wx/xLightsMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
#include "setup/IPEntryDialog.h"
#include "media/JukeboxPanel.h"
#include "app-shell/KeyBindingEditDialog.h"
#include "app-shell/KeyBindingsDialog.h"
#include "layout/ControllerListPanel.h"
#include "layout/LayoutGroup.h"
#include "layout/LayoutPanel.h"
Expand Down Expand Up @@ -8860,9 +8861,14 @@ void xLightsFrame::OnMenuItemBulkControllerUploadSelected(wxCommandEvent& event)

void xLightsFrame::OnMenuItem_KeyBindingsSelected(wxCommandEvent& event)
{
KeyBindingEditDialog dlg(this, &GetMainSequencer()->keyBindings, &effectManager);

dlg.ShowModal();
// Modeless so it can stay open while you keep working. Reuse the open
// instance rather than stacking duplicates.
if (_keyBindingsDialog != nullptr) {
_keyBindingsDialog->Raise();
return;
}
_keyBindingsDialog = new KeyBindingsDialog(this, this, [this]() { _keyBindingsDialog = nullptr; });
_keyBindingsDialog->Show();
}

void xLightsFrame::OnMenuItem_ExportControllerConnectionsSelected(wxCommandEvent& event)
Expand Down
2 changes: 2 additions & 0 deletions src-ui-wx/xLightsMain.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class ControllerCaps;
class Discovery;
class DiscoveryDelegate;
class EffectTreeDialog;
class KeyBindingsDialog;
class FPP;
class ConvertDialog;
class ConvertLogDialog;
Expand Down Expand Up @@ -1958,6 +1959,7 @@ private :
std::vector<ModelPreview *> PreviewWindows;
ColorManager color_mgr;
EffectTreeDialog *EffectTreeDlg = nullptr;
KeyBindingsDialog* _keyBindingsDialog = nullptr; // modeless; nulled on close
bool _effectPresetsInitialized = false;

ModelGroup* GetSelectedModelGroup() const;
Expand Down
3 changes: 3 additions & 0 deletions xLights/Xlights.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,7 @@ xcopy /Y /S /I "$(SolutionDir)..\resources\effectmetadata\*" "$(TargetDir)effect
<ClCompile Include="..\src-ui-wx\preferences\CheckSequenceSettingsPanel.cpp" />
<ClCompile Include="..\src-ui-wx\preferences\ColorManagerSettingsPanel.cpp" />
<ClCompile Include="..\src-ui-wx\preferences\EffectsGridSettingsPanel.cpp" />
<ClCompile Include="..\src-ui-wx\preferences\KeyBindingsSettingsPanel.cpp" />
<ClCompile Include="..\src-ui-wx\preferences\OtherSettingsPanel.cpp" />
<ClCompile Include="..\src-ui-wx\preferences\ToolbarLayout.cpp" />
<ClCompile Include="..\src-ui-wx\preferences\ToolbarsSettingsPanel.cpp" />
Expand Down Expand Up @@ -1538,6 +1539,7 @@ xcopy /Y /S /I "$(SolutionDir)..\resources\effectmetadata\*" "$(TargetDir)effect
<ClInclude Include="..\src-core\utils\AppCallbacks.h" />
<ClInclude Include="..\src-core\utils\JobPool.h" />
<ClInclude Include="..\src-ui-wx\app-shell\KeyBindings.h" />
<ClInclude Include="..\src-ui-wx\app-shell\KeyBindingsDialog.h" />
<ClInclude Include="..\src-ui-wx\layout\LayoutGroup.h" />
<ClInclude Include="..\src-ui-wx\layout\LayoutPanel.h" />
<ClInclude Include="..\src-ui-wx\layout\ReplaceModelDialog.h" />
Expand Down Expand Up @@ -1631,6 +1633,7 @@ xcopy /Y /S /I "$(SolutionDir)..\resources\effectmetadata\*" "$(TargetDir)effect
<ClInclude Include="..\src-ui-wx\preferences\CheckSequenceSettingsPanel.h" />
<ClInclude Include="..\src-ui-wx\preferences\ColorManagerSettingsPanel.h" />
<ClInclude Include="..\src-ui-wx\preferences\EffectsGridSettingsPanel.h" />
<ClInclude Include="..\src-ui-wx\preferences\KeyBindingsSettingsPanel.h" />
<ClInclude Include="..\src-ui-wx\preferences\OtherSettingsPanel.h" />
<ClInclude Include="..\src-ui-wx\preferences\ToolbarLayout.h" />
<ClInclude Include="..\src-ui-wx\preferences\ToolbarsSettingsPanel.h" />
Expand Down
7 changes: 7 additions & 0 deletions xLights/Xlights.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,9 @@
<ClCompile Include="..\src-ui-wx\preferences\EffectsGridSettingsPanel.cpp">
<Filter>Preferences</Filter>
</ClCompile>
<ClCompile Include="..\src-ui-wx\preferences\KeyBindingsSettingsPanel.cpp">
<Filter>Preferences</Filter>
</ClCompile>
<ClCompile Include="..\src-ui-wx\preferences\RandomEffectsSettingsPanel.cpp">
<Filter>Preferences</Filter>
</ClCompile>
Expand Down Expand Up @@ -1615,6 +1618,7 @@
<ClInclude Include="..\src-core\utils\AppCallbacks.h" />
<ClInclude Include="..\src-core\utils\JobPool.h" />
<ClInclude Include="..\src-ui-wx\app-shell\KeyBindings.h" />
<ClInclude Include="..\src-ui-wx\app-shell\KeyBindingsDialog.h" />
<ClInclude Include="..\src-ui-wx\layout\LayoutGroup.h" />
<ClInclude Include="..\src-ui-wx\layout\LayoutPanel.h" />
<ClInclude Include="..\src-ui-wx\layout\ReplaceModelDialog.h" />
Expand Down Expand Up @@ -2230,6 +2234,9 @@
<ClInclude Include="..\src-ui-wx\preferences\EffectsGridSettingsPanel.h">
<Filter>Preferences</Filter>
</ClInclude>
<ClInclude Include="..\src-ui-wx\preferences\KeyBindingsSettingsPanel.h">
<Filter>Preferences</Filter>
</ClInclude>
<ClInclude Include="..\src-ui-wx\preferences\RandomEffectsSettingsPanel.h">
<Filter>Preferences</Filter>
</ClInclude>
Expand Down
3 changes: 3 additions & 0 deletions xLights/xLights.cbp
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@
<Unit filename="../src-ui-wx/app-shell/KeyBindingEditDialog.h" />
<Unit filename="../src-ui-wx/app-shell/KeyBindings.cpp" />
<Unit filename="../src-ui-wx/app-shell/KeyBindings.h" />
<Unit filename="../src-ui-wx/app-shell/KeyBindingsDialog.h" />
<Unit filename="../src-ui-wx/import_export/LMSImportChannelMapDialog.cpp" />
<Unit filename="../src-ui-wx/import_export/LMSImportChannelMapDialog.h" />
<Unit filename="../src-core/import_export/ExportModels.cpp" />
Expand Down Expand Up @@ -1389,6 +1390,8 @@
<Unit filename="../src-ui-wx/preferences/ColorManagerSettingsPanel.h" />
<Unit filename="../src-ui-wx/preferences/EffectsGridSettingsPanel.cpp" />
<Unit filename="../src-ui-wx/preferences/EffectsGridSettingsPanel.h" />
<Unit filename="../src-ui-wx/preferences/KeyBindingsSettingsPanel.cpp" />
<Unit filename="../src-ui-wx/preferences/KeyBindingsSettingsPanel.h" />
<Unit filename="../src-ui-wx/preferences/OtherSettingsPanel.cpp" />
<Unit filename="../src-ui-wx/preferences/OtherSettingsPanel.h" />
<Unit filename="../src-ui-wx/preferences/ToolbarLayout.cpp" />
Expand Down
Loading