Skip to content

Commit d9a5771

Browse files
committed
fix(ui): update Quick Key Settings, refine Quick Load Preset logic, fix SoundBoard sync and Manage Projects button
1 parent 10f2c9d commit d9a5771

15 files changed

+598
-550
lines changed

Resources/Languages/lang_en.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@
1313
},
1414
"presetbar": {
1515
"presetRunning": "Preset Running:",
16-
"quickChoice": "Quick Choice:",
16+
"quickChoice": "Quick Load:",
1717
"load": "Load",
1818
"noPresetLoaded": "No Preset Loaded",
19+
"saveTitle": "Save Preset",
20+
"saveMessage": "A preset is currently loaded. How would you like to save?",
21+
"saveOverwrite": "Save (Overwrite)",
22+
"saveAsNew": "Save As New...",
1923
"overwriteTitle": "Overwrite Preset",
2024
"overwriteMessage": "A preset named '{{presetName}}' already exists. Do you want to overwrite it?",
2125
"saveAsTitle": "Save Preset As...",
2226
"yes": "Yes",
23-
"no": "No"
27+
"no": "No",
28+
"emptySlot": "Slot",
29+
"assignPreset": "Assign Preset to this Slot",
30+
"clearSlot": "Clear this Slot"
2431
},
2532
"tracks": {
2633
"vocal": "VOCAL",
@@ -177,6 +184,10 @@
177184
"delete": "Delete",
178185
"deleteConfirmTitle": "Confirm Deletion",
179186
"deleteConfirmMessage": "Are you sure you want to permanently delete the project '{{projectName}}' and all its audio files?"
187+
},
188+
"fxChain": {
189+
"send": "Send:",
190+
"return": "Return:"
180191
},
181192
"lock": {
182193
"unlockTitle": "Unlock System",

Resources/Languages/lang_vi.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@
1313
},
1414
"presetbar": {
1515
"presetRunning": "Preset đang chạy:",
16-
"quickChoice": "Chọn nhanh:",
16+
"quickChoice": "Tải Nhanh:",
1717
"load": "Tải",
1818
"noPresetLoaded": "Chưa tải Preset",
19+
"saveTitle": "Lưu Preset",
20+
"saveMessage": "Một preset đang được tải. Bạn muốn lưu như thế nào?",
21+
"saveOverwrite": "Lưu (Ghi đè)",
22+
"saveAsNew": "Lưu với tên mới...",
1923
"overwriteTitle": "Ghi đè Preset",
2024
"overwriteMessage": "Một preset có tên '{{presetName}}' đã tồn tại. Bạn có muốn ghi đè lên nó không?",
2125
"saveAsTitle": "Lưu Preset với tên...",
2226
"yes": "Đồng ý",
23-
"no": "Không"
27+
"no": "Không",
28+
"emptySlot": "Ô",
29+
"assignPreset": "Gán Preset vào ô này",
30+
"clearSlot": "Xóa gán ở ô này"
2431
},
2532
"tracks": {
2633
"vocal": "GIỌNG HÁT",
@@ -177,6 +184,10 @@
177184
"delete": "Xóa",
178185
"deleteConfirmTitle": "Xác Nhận Xóa",
179186
"deleteConfirmMessage": "Bạn có chắc chắn muốn xóa vĩnh viễn project '{{projectName}}' và toàn bộ các file audio bên trong không?"
187+
},
188+
"fxChain": {
189+
"send": "Gửi đi:",
190+
"return": "Nhận về:"
180191
},
181192
"lock": {
182193
"unlockTitle": "Mở khóa Hệ thống",

Source/Data/AppState.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "../GUI/Layout/TrackComponent.h"
66
#include "../GUI/Layout/MasterUtilityComponent.h"
77
#include "../GUI/Layout/PresetBarComponent.h"
8+
// <<< FIX: Include the full definition for MenubarComponent >>>
89
#include "../GUI/Layout/MenubarComponent.h"
910
#include "juce_audio_devices/juce_audio_devices.h"
1011
#include <juce_cryptography/juce_cryptography.h>
@@ -23,6 +24,7 @@ namespace SessionIds
2324
const juce::Identifier ACTIVE_PRESET("ACTIVE_PRESET");
2425
const juce::Identifier presetName("name");
2526
const juce::Identifier OPEN_WINDOWS("OPEN_WINDOWS");
27+
const juce::Identifier QUICK_PRESETS("QUICK_PRESETS");
2628
}
2729

2830
namespace WindowStateIds
@@ -39,6 +41,9 @@ AppState& AppState::getInstance()
3941
AppState::AppState()
4042
{
4143
currentPresetName = LanguageManager::getInstance().get("presetbar.noPresetLoaded");
44+
// <<< MODIFIED: Initialize for 5 quick preset slots >>>
45+
for (int i = 0; i < numQuickSlots; ++i)
46+
quickPresetSlots.add({});
4247
}
4348

4449
AppState::~AppState() {}
@@ -91,6 +96,7 @@ juce::File AppState::getSessionFile() const
9196

9297
void AppState::saveState(MainComponent& mainComponent)
9398
{
99+
// ... (code to save other settings is unchanged) ...
94100
auto& deviceManager = mainComponent.getAudioDeviceManager();
95101
auto& audioEngine = mainComponent.getAudioEngine();
96102

@@ -112,6 +118,13 @@ void AppState::saveState(MainComponent& mainComponent)
112118
auto* presetStateXml = sessionXml->createNewChildElement(SessionIds::ACTIVE_PRESET);
113119
presetStateXml->setAttribute(SessionIds::presetName, getCurrentPresetName());
114120

121+
// <<< MODIFIED: Save 5 quick preset assignments >>>
122+
auto* quickPresetsXml = sessionXml->createNewChildElement(SessionIds::QUICK_PRESETS);
123+
for (int i = 0; i < quickPresetSlots.size(); ++i)
124+
{
125+
quickPresetsXml->setAttribute("slot" + juce::String(i), quickPresetSlots[i]);
126+
}
127+
115128
auto* openWindowsStateXml = sessionXml->createNewChildElement(SessionIds::OPEN_WINDOWS);
116129
auto vocalWindowsState = mainComponent.getVocalTrack().getOpenWindowsState();
117130
if (vocalWindowsState.getNumChildren() > 0)
@@ -154,32 +167,40 @@ void AppState::loadPostDeviceState(MainComponent& mainComponent)
154167

155168
if (auto xml = juce::parseXML(sessionFile))
156169
{
170+
// ... (loading routing state is unchanged) ...
157171
auto* routingStateXml = xml->getChildByName(SessionIds::ROUTING);
158172
if (routingStateXml != nullptr)
159173
{
160174
auto vocalInputName = routingStateXml->getStringAttribute(SessionIds::VOCAL_INPUT);
161175
auto musicInputName = routingStateXml->getStringAttribute(SessionIds::MUSIC_INPUT);
162176
auto appOutputName = routingStateXml->getStringAttribute(SessionIds::APP_OUTPUT);
163177

164-
// Cập nhật AudioEngine (không đổi)
165178
mainComponent.getAudioEngine().setVocalInputChannelByName(vocalInputName);
166179
mainComponent.getAudioEngine().setMusicInputChannelByName(musicInputName);
167180
mainComponent.getAudioEngine().setSelectedOutputChannelsByName(appOutputName);
168181

169-
// Cập nhật giao diện thông qua các component mới
170182
if (auto* vocalSelector = mainComponent.getVocalTrack().getChannelSelector())
171183
vocalSelector->setSelectedChannelByName(vocalInputName);
172184

173185
if (auto* musicSelector = mainComponent.getMusicTrack().getChannelSelector())
174186
musicSelector->setSelectedChannelByName(musicInputName);
175187

176-
// <<< SỬA: Cách cập nhật MenubarComponent >>>
177188
if (auto* menubar = mainComponent.getMenubarComponent())
178189
if (auto* outputSelector = menubar->getOutputSelector())
179190
outputSelector->setSelectedChannelByName(appOutputName);
180191
}
181192

182-
// ... (phần còn lại của hàm không đổi) ...
193+
// <<< MODIFIED: Load 5 quick preset assignments >>>
194+
auto* quickPresetsXml = xml->getChildByName(SessionIds::QUICK_PRESETS);
195+
if (quickPresetsXml != nullptr)
196+
{
197+
for (int i = 0; i < quickPresetSlots.size(); ++i)
198+
{
199+
quickPresetSlots.set(i, quickPresetsXml->getStringAttribute("slot" + juce::String(i), {}));
200+
}
201+
}
202+
203+
// ... (loading active preset and open windows is unchanged) ...
183204
auto* presetStateXml = xml->getChildByName(SessionIds::ACTIVE_PRESET);
184205
if (presetStateXml != nullptr)
185206
{
@@ -258,4 +279,29 @@ void AppState::loadLockState(bool isLocked, const juce::String& passwordHash)
258279
systemLocked = isLocked;
259280
lockPasswordHash = passwordHash;
260281
sendChangeMessage();
282+
}
283+
284+
// <<< ADDED: Implementation for Quick Preset Slot Management >>>
285+
void AppState::assignQuickPreset(int slotIndex, const juce::String& presetName)
286+
{
287+
if (juce::isPositiveAndBelow(slotIndex, quickPresetSlots.size()))
288+
{
289+
if (quickPresetSlots[slotIndex] != presetName)
290+
{
291+
quickPresetSlots.set(slotIndex, presetName);
292+
sendChangeMessage(); // Notify listeners (like PresetBarComponent)
293+
}
294+
}
295+
}
296+
297+
juce::String AppState::getQuickPresetName(int slotIndex) const
298+
{
299+
if (juce::isPositiveAndBelow(slotIndex, quickPresetSlots.size()))
300+
return quickPresetSlots[slotIndex];
301+
return {};
302+
}
303+
304+
int AppState::getNumQuickPresetSlots() const
305+
{
306+
return numQuickSlots;
261307
}

Source/Data/AppState.h

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ class MainComponent;
77
class AppState : public juce::ChangeBroadcaster
88
{
99
public:
10+
// <<< ADDED: A constant for the number of slots >>>
11+
static constexpr int numQuickSlots = 5;
12+
1013
static AppState& getInstance();
1114

1215
void setPresetDirty(bool dirty);
@@ -16,19 +19,10 @@ class AppState : public juce::ChangeBroadcaster
1619
void markAsSaved(const juce::String& newName);
1720

1821
// --- CÁC HÀM CHO TÍNH NĂNG KHÓA ---
19-
/** Locks the system with a given password. The password should be plain text. */
2022
void setSystemLocked(bool shouldBeLocked, const juce::String& password = {});
21-
22-
/** Unlocks the system if the provided password is correct. */
2323
bool unlockSystem(const juce::String& passwordAttempt);
24-
25-
/** Checks if the system is currently locked. */
2624
bool isSystemLocked() const;
27-
28-
/** Returns the MD5 hash of the current lock password. */
2925
juce::String getPasswordHash() const;
30-
31-
/** Loads lock state from a preset file. */
3226
void loadLockState(bool isLocked, const juce::String& passwordHash);
3327

3428

@@ -38,16 +32,25 @@ class AppState : public juce::ChangeBroadcaster
3832
void loadPostDeviceState(MainComponent& mainComponent);
3933
juce::File getSessionFile() const;
4034

35+
// <<< MODIFIED: Updated comments and consistency >>>
36+
/** Assigns a preset name to a quick load slot. */
37+
void assignQuickPreset(int slotIndex, const juce::String& presetName);
38+
/** Gets the name of the preset assigned to a slot. */
39+
juce::String getQuickPresetName(int slotIndex) const;
40+
/** Gets the number of available quick slots. */
41+
int getNumQuickPresetSlots() const;
42+
4143
private:
4244
AppState();
4345
~AppState() override;
4446

4547
bool dirty = false;
4648
juce::String currentPresetName;
4749

48-
// --- BIẾN THÀNH VIÊN CHO TÍNH NĂNG KHÓA ---
4950
std::atomic<bool> systemLocked{ false };
5051
juce::String lockPasswordHash;
5152

53+
juce::StringArray quickPresetSlots;
54+
5255
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AppState)
5356
};

Source/GUI/Components/ProjectManagerComponent.cpp

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ ProjectManagerComponent::ProjectManagerComponent(AudioEngine& engine) : audioEng
3232
addAndMakeVisible(stopProjectButton);
3333
stopProjectButton.onClick = [this] { stopProject(); };
3434

35-
// <<< THÊM: Khởi tạo và cấu hình các label >>>
3635
addAndMakeVisible(loadedProjectTitleLabel);
3736
loadedProjectTitleLabel.setFont(IdolUIHelpers::createRegularFont(14.0f));
3837
loadedProjectTitleLabel.setColour(juce::Label::textColourId, juce::Colours::lightgrey);
@@ -49,7 +48,7 @@ ProjectManagerComponent::ProjectManagerComponent(AudioEngine& engine) : audioEng
4948

5049
ProjectManagerComponent::~ProjectManagerComponent()
5150
{
52-
// unique_ptr will handle deletion automatically.
51+
// unique_ptr handles the destruction automatically
5352
LanguageManager::getInstance().removeChangeListener(this);
5453
audioEngine.getProjectState().removeListener(this);
5554
}
@@ -62,23 +61,19 @@ void ProjectManagerComponent::paint(juce::Graphics& g)
6261
g.drawRoundedRectangle(getLocalBounds().toFloat(), 5.0f, 1.0f);
6362
}
6463

65-
// <<< SỬA: Thêm static_cast<int> để loại bỏ cảnh báo >>>
6664
void ProjectManagerComponent::resized()
6765
{
6866
auto bounds = getLocalBounds().reduced(10);
6967

70-
// Chia thành 2 cột: trái cho thông tin, phải cho các nút
71-
auto rightArea = bounds.removeFromRight(450); // Dành không gian rộng hơn cho các nút
68+
auto rightArea = bounds.removeFromRight(450);
7269
auto leftArea = bounds;
7370

74-
// Layout cho cột trái
7571
loadedProjectTitleLabel.setBounds(leftArea.removeFromTop(leftArea.getHeight() / 2));
7672
loadedProjectLabel.setBounds(leftArea);
7773

78-
// Layout cho cột phải (4 nút trên cùng một hàng)
7974
juce::FlexBox fb;
80-
fb.justifyContent = juce::FlexBox::JustifyContent::flexEnd; // Căn lề phải
81-
fb.alignItems = juce::FlexBox::AlignItems::stretch; // Các nút cao bằng nhau
75+
fb.justifyContent = juce::FlexBox::JustifyContent::flexEnd;
76+
fb.alignItems = juce::FlexBox::AlignItems::stretch;
8277

8378
const float gap = 10.0f;
8479

@@ -106,20 +101,17 @@ void ProjectManagerComponent::valueTreePropertyChanged(juce::ValueTree& tree, co
106101
bool isPlaying = tree.getProperty(ProjectStateIDs::isPlaying);
107102
juce::String projectName = tree.getProperty(ProjectStateIDs::name).toString();
108103

109-
// Label "Loaded:" luôn hiển thị
110104
loadedProjectTitleLabel.setText(lang.get("projectManager.loaded"), juce::dontSendNotification);
111105

112106
if (projectName.isNotEmpty())
113107
{
114-
// Nếu có project, hiển thị tên và cập nhật các nút
115108
loadedProjectLabel.setText(projectName, juce::dontSendNotification);
116109
playProjectButton.setEnabled(!isPlaying);
117110
stopProjectButton.setEnabled(isPlaying);
118111
recordProjectButton.setEnabled(false);
119112
}
120113
else
121114
{
122-
// Nếu không có project, hiển thị placeholder và cập nhật các nút
123115
loadedProjectLabel.setText(lang.get("projectManager.noProjectLoaded"), juce::dontSendNotification);
124116
playProjectButton.setEnabled(false);
125117
stopProjectButton.setEnabled(false);
@@ -140,7 +132,6 @@ void ProjectManagerComponent::updateTexts()
140132
playProjectButton.setButtonText(lang.get("projectManager.play"));
141133
stopProjectButton.setButtonText(lang.get("projectManager.stop"));
142134

143-
// <<< SỬA: Cập nhật lại cả hai label khi đổi ngôn ngữ >>>
144135
valueTreePropertyChanged(audioEngine.getProjectState(), {});
145136
}
146137

@@ -194,24 +185,24 @@ void ProjectManagerComponent::stopProject()
194185
audioEngine.stopLoadedProject();
195186
}
196187

197-
// <<< MODIFIED: Now uses unique_ptr and a callback for lifecycle management >>>
188+
// <<< MODIFIED: This now correctly manages the window lifecycle >>>
198189
void ProjectManagerComponent::manageProjects()
199190
{
200191
if (projectListWindow == nullptr)
201192
{
193+
// Create the window and give it a callback to run on close.
194+
// The callback safely resets this component's unique_ptr.
202195
projectListWindow = std::make_unique<ProjectListWindow>(
203196
"Projects",
204197
audioEngine,
205198
[this](const juce::File& projectJsonFile) {
206199
audioEngine.loadProject(projectJsonFile);
207200
},
208-
[this] { // on close callback
201+
[this] { // on window close
209202
projectListWindow.reset();
210203
}
211204
);
212205
}
213-
else
214-
{
215-
projectListWindow->toFront(true);
216-
}
206+
207+
projectListWindow->toFront(true);
217208
}

Source/GUI/Components/ProjectManagerComponent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class ProjectManagerComponent : public juce::Component,
2929

3030
AudioEngine& audioEngine;
3131

32-
// <<< MODIFIED: Changed from SafePointer to unique_ptr for ownership >>>
32+
// <<< MODIFIED: Changed from SafePointer to unique_ptr for robust ownership >>>
3333
std::unique_ptr<ProjectListWindow> projectListWindow;
3434

3535
juce::Label loadedProjectTitleLabel;

0 commit comments

Comments
 (0)