Skip to content

Commit 10fbd11

Browse files
committed
Show a Sub Mixer's tracks in the Device Rack
- List the tracks its member devices play on - Give the track list more room before eliding it
1 parent 8712192 commit 10fbd11

6 files changed

Lines changed: 105 additions & 8 deletions

File tree

‎CHANGELOG‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ Bug fixes:
193193
* Speech's Line mode now fits a line to its note in a render as well as in
194194
playback, having always fallen back to Length when rendering
195195

196+
Other:
197+
198+
* The Device Rack lists the tracks a Sub Mixer carries, and gives the track
199+
list more room before cutting it short
200+
196201
8.0.0
197202
=====
198203

‎src/unit_tests/device_rack_controller_test/device_rack_controller_test.cpp‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,57 @@ void DeviceRackControllerTest::test_subMixerCandidates_shouldCarryTrackNames()
265265
QVERIFY(!entry["typeName"].toString().isEmpty());
266266
}
267267

268+
void DeviceRackControllerTest::test_usageSummary_device_shouldReturnTrackNames()
269+
{
270+
const auto audioEngine = std::make_shared<AudioEngine>();
271+
const auto deviceService = std::make_shared<DeviceService>(audioEngine, std::make_shared<DataService>());
272+
const auto deviceName = "Device 1";
273+
deviceService->setDevice(0, std::make_shared<MockDevice>(deviceName));
274+
275+
const auto editorService = std::make_shared<MockEditorService>();
276+
editorService->setMockIndices({ 0 });
277+
editorService->setMockTrackName(0, "Drums");
278+
editorService->setMockInstrumentPortName(0, QString::fromStdString(deviceName));
279+
280+
DeviceRackController controller { deviceService, {}, editorService };
281+
282+
QCOMPARE(controller.usageSummary(0), QString { "Drums" });
283+
}
284+
285+
void DeviceRackControllerTest::test_usageSummary_subMixer_shouldReturnMemberTrackNames()
286+
{
287+
const auto audioEngine = std::make_shared<AudioEngine>();
288+
const auto deviceService = std::make_shared<DeviceService>(audioEngine, std::make_shared<DataService>());
289+
deviceService->setDevice(0, std::make_shared<MockDevice>("Device 1"));
290+
deviceService->setDevice(1, std::make_shared<MockDevice>("Device 2"));
291+
deviceService->setDevice(2, std::make_shared<MockDevice>("Device 3"));
292+
deviceService->setDevice(3, std::make_shared<SubMixerDevice>("Inner"));
293+
deviceService->setDevice(4, std::make_shared<SubMixerDevice>("Outer"));
294+
295+
const auto editorService = std::make_shared<MockEditorService>();
296+
editorService->setMockIndices({ 0, 1, 2, 3 });
297+
editorService->setMockTrackName(0, "Drums");
298+
editorService->setMockInstrumentPortName(0, "Device 1");
299+
editorService->setMockTrackName(1, "Bass");
300+
editorService->setMockInstrumentPortName(1, "Device 2");
301+
editorService->setMockTrackName(2, "Bass, doubled");
302+
editorService->setMockInstrumentPortName(2, "Device 2");
303+
editorService->setMockTrackName(3, "Pads");
304+
editorService->setMockInstrumentPortName(3, "Device 3");
305+
306+
DeviceRackController controller { deviceService, {}, editorService };
307+
308+
QCOMPARE(controller.usageSummary(4), QString {});
309+
310+
QVERIFY(controller.addSubMixerMember(3, 2));
311+
QVERIFY(controller.addSubMixerMember(4, 0));
312+
QVERIFY(controller.addSubMixerMember(4, 1));
313+
QVERIFY(controller.addSubMixerMember(4, 3));
314+
QCOMPARE(controller.usageSummary(3), QString { "Pads" });
315+
// A nested SubMixer contributes the tracks of its own members.
316+
QCOMPARE(controller.usageSummary(4), QString { "Drums, Bass, Bass, doubled, Pads" });
317+
}
318+
268319
void DeviceRackControllerTest::test_deviceGain_shouldDefaultToUnityAndRoundTrip()
269320
{
270321
const auto audioEngine = std::make_shared<AudioEngine>();

‎src/unit_tests/device_rack_controller_test/device_rack_controller_test.hpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ private slots:
3131
void test_devices_shouldReturnDeviceNames();
3232
void test_trackNames_shouldReturnTrackNamesForDevice();
3333
void test_subMixerCandidates_shouldCarryTrackNames();
34+
void test_usageSummary_device_shouldReturnTrackNames();
35+
void test_usageSummary_subMixer_shouldReturnMemberTrackNames();
3436
void test_deviceGain_shouldDefaultToUnityAndRoundTrip();
3537
void test_setDevice_shouldAddDeviceAndNotify();
3638
void test_replaceDevice_shouldKeepInsertEffectsAndNotify();

‎src/view/controllers/device_rack_controller.cpp‎

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,19 +462,43 @@ QString DeviceRackController::deviceName(int slotIndex) const
462462
return "";
463463
}
464464

465-
QString DeviceRackController::trackNames(int slotIndex) const
465+
QStringList DeviceRackController::trackNameList(int slotIndex) const
466466
{
467+
QStringList trackNames;
467468
if (const auto device = m_deviceService->device(static_cast<size_t>(slotIndex))) {
468469
const auto deviceName = QString::fromStdString(device->name());
469-
QStringList trackNames;
470470
for (const auto index : m_editorService->trackIndices()) {
471471
if (const auto portName = m_editorService->instrumentPortName(index); portName == deviceName) {
472472
trackNames << m_editorService->trackName(index);
473473
}
474474
}
475-
return trackNames.join(", ");
476475
}
477-
return "";
476+
return trackNames;
477+
}
478+
479+
QString DeviceRackController::trackNames(int slotIndex) const
480+
{
481+
return trackNameList(slotIndex).join(", ");
482+
}
483+
484+
QStringList DeviceRackController::usageTrackNames(int slotIndex) const
485+
{
486+
// A SubMixer plays nothing itself; what it carries is whatever its members play, so it borrows
487+
// their tracks, through any SubMixer nested inside it. DeviceService rules out cycles.
488+
if (const auto subMixer = std::dynamic_pointer_cast<SubMixerDevice>(m_deviceService->device(static_cast<size_t>(slotIndex)))) {
489+
QStringList names;
490+
for (const auto memberSlot : subMixer->members()) {
491+
names << usageTrackNames(static_cast<int>(memberSlot));
492+
}
493+
names.removeDuplicates();
494+
return names;
495+
}
496+
return trackNameList(slotIndex);
497+
}
498+
499+
QString DeviceRackController::usageSummary(int slotIndex) const
500+
{
501+
return usageTrackNames(slotIndex).join(", ");
478502
}
479503

480504
QVariantList DeviceRackController::availableDevices() const

‎src/view/controllers/device_rack_controller.hpp‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ class DeviceRackController : public QAbstractListModel
8989
Q_INVOKABLE QString deviceTypeName(int slotIndex) const;
9090
Q_INVOKABLE QString deviceName(int slotIndex) const;
9191
Q_INVOKABLE QString trackNames(int slotIndex) const;
92+
//! What the Device Rack shows beside a device: the tracks it plays on, or for a SubMixer the
93+
//! tracks its members play on.
94+
Q_INVOKABLE QString usageSummary(int slotIndex) const;
9295
Q_INVOKABLE QVariantList availableDevices() const;
9396

9497
//! Every occupied slot a SubMixer could take, with whether it already belongs to one.
@@ -193,6 +196,8 @@ class DeviceRackController : public QAbstractListModel
193196

194197
private:
195198
QString trackNames(const QString & deviceName) const;
199+
QStringList trackNameList(int slotIndex) const;
200+
QStringList usageTrackNames(int slotIndex) const;
196201

197202
//! Push the current gate onto every device's taps. Needed after any slot change, because a
198203
//! freshly created device starts with its meters off regardless of what is on screen.

‎src/view/qml/Dialogs/DeviceRackDialog.qml‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ AnimatedDialog {
102102
deviceRackController.revision;
103103
return deviceRackController.deviceName(index);
104104
}
105-
readonly property string trackNames: {
105+
readonly property string usageSummary: {
106106
deviceRackController.revision;
107-
return deviceRackController.trackNames(index);
107+
return deviceRackController.usageSummary(index);
108108
}
109109
readonly property var meterLevels: {
110110
deviceListView.meterTick;
@@ -177,19 +177,29 @@ AnimatedDialog {
177177
// The buttons and meters on the row have fixed widths, so the name is what
178178
// gives way on a narrow rack instead of running over them
179179
Layout.fillWidth: true
180+
Layout.preferredWidth: implicitWidth
180181
Layout.minimumWidth: 0
181182
elide: Text.ElideRight
182183
visible: deviceType !== ""
183184
}
184185

185186
Text {
186-
text: trackNames
187+
text: usageSummary
187188
color: "#aaa"
188189
font.pointSize: 11
189-
Layout.preferredWidth: 150
190+
// Shares the slack with the name in proportion to their lengths, so a long
191+
// list of tracks is not cut to a fixed stub while the name has room to spare
192+
Layout.fillWidth: true
193+
Layout.preferredWidth: implicitWidth
194+
Layout.minimumWidth: 0
190195
horizontalAlignment: Text.AlignRight
191196
elide: Text.ElideRight
192197
visible: deviceType !== ""
198+
ToolTip.visible: truncated && summaryHover.hovered
199+
ToolTip.text: usageSummary
200+
HoverHandler {
201+
id: summaryHover
202+
}
193203
}
194204

195205
Image {

0 commit comments

Comments
 (0)