Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,8 @@ docs/*
!docs/model-licensing-audit.json



# README screenshots that are captured but not embedded
assets/screenshots/model-hub.png
assets/screenshots/settings.png
assets/screenshots/shortcuts.png
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,10 @@ if(BUILD_TESTING)
tests/unit/TestMain.cpp
tests/unit/ControllerTests.cpp
tests/unit/TaskCenterPanelTests.cpp
tests/unit/ControlDeckTests.cpp
tests/unit/UiDecisionTests.cpp
tests/unit/SessionSerializationTests.cpp
src/app/ui/SessionManager.cpp
tests/unit/AudioIoTests.cpp
tests/unit/AnalysisTests.cpp
tests/unit/StemOriginSafetyTests.cpp
Expand Down Expand Up @@ -590,6 +593,9 @@ if(BUILD_TESTING)
tests/integration/PipelineIntegrationTests.cpp
src/app/ui/TaskCenterPanel.cpp
src/app/style/AutoMixLookAndFeel.cpp
src/app/ui/ControlDeck.cpp
src/app/ui/StemPanel.cpp
src/app/ui/GlowMeters.cpp
src/app/controllers/ModelController.cpp
src/app/controllers/ImportController.cpp
src/app/controllers/ExportController.cpp
Expand Down
524 changes: 220 additions & 304 deletions README.md

Large diffs are not rendered by default.

Binary file added assets/screenshots/main-empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/screenshots/main-session.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions src/app/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ class MainWindow final : public juce::DocumentWindow {
setVisible(true);
}

bool requestQuit(std::function<void()> quitNow) {
if (auto* layout = dynamic_cast<MainLayout*>(getContentComponent()))
return layout->requestQuit(std::move(quitNow));
quitNow();
return true;
}

void closeButtonPressed() override {
juce::JUCEApplication::getInstance()->systemRequestedQuit();
}
Expand All @@ -50,6 +57,14 @@ class AutoMixMasterApplication final : public juce::JUCEApplication {
automix::ai::OrtRuntime::instance().warmUpAsync();
}

void systemRequestedQuit() override {
if (mainWindow_ == nullptr) {
quit();
return;
}
mainWindow_->requestQuit([] { juce::JUCEApplication::quit(); });
}

void shutdown() override {
// Let the ONNX Runtime warm-up thread finish before the window and libraries tear down.
automix::ai::OrtRuntime::instance().joinWarmUp();
Expand Down
71 changes: 55 additions & 16 deletions src/app/style/AutoMixLookAndFeel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,28 +68,58 @@ AutoMixLookAndFeel::AutoMixLookAndFeel() {
// Button
// ─────────────────────────────────────────────────────────────────

namespace {
juce::String variantOf(const juce::Button& button) {
return button.getProperties()[buttonVariant::key].toString();
}
} // namespace

void AutoMixLookAndFeel::drawButtonBackground(juce::Graphics& g, juce::Button& button,
const juce::Colour& /*backgroundColour*/,
bool shouldDrawButtonAsHighlighted,
bool shouldDrawButtonAsDown) {
auto bounds = button.getLocalBounds().toFloat().reduced(0.5f);
auto cornerSize = metrics::cornerRadius;

juce::Colour bg;
if (!button.isEnabled()) {
bg = colour(colours::surfaceBorder);
} else if (shouldDrawButtonAsDown) {
bg = colour(colours::primaryPressed);
} else if (shouldDrawButtonAsHighlighted) {
bg = colour(colours::primary).interpolatedWith(colour(colours::primaryHover), 0.6f);
} else if (button.getToggleState()) {
bg = colour(colours::primary);
const auto variant = variantOf(button);
const bool enabled = button.isEnabled();

if (!enabled) {
// Disabled: no fill, outline only for primary/secondary so it never out-shouts an enabled button.
if (variant != buttonVariant::quiet && variant != buttonVariant::danger) {
g.setColour(colour(colours::surfaceBorder));
g.drawRoundedRectangle(bounds, cornerSize, 1.0f);
}
} else if (variant == buttonVariant::secondary) {
juce::Colour bg = colour(colours::surfaceLight);
if (enabled && shouldDrawButtonAsDown)
bg = bg.darker(0.1f);
else if (enabled && shouldDrawButtonAsHighlighted)
bg = bg.brighter(0.1f);
g.setColour(bg);
g.fillRoundedRectangle(bounds, cornerSize);
g.setColour(colour(colours::surfaceBorder));
g.drawRoundedRectangle(bounds, cornerSize, 1.0f);
} else if (variant == buttonVariant::quiet || variant == buttonVariant::danger) {
if (enabled && (shouldDrawButtonAsHighlighted || shouldDrawButtonAsDown)) {
g.setColour(variant == buttonVariant::quiet ? colour(colours::surfaceLight)
: colour(colours::error).withAlpha(0.15f));
g.fillRoundedRectangle(bounds, cornerSize);
}
} else {
bg = colour(colours::primary);
}
juce::Colour bg;
if (shouldDrawButtonAsDown) {
bg = colour(colours::primaryPressed);
} else if (shouldDrawButtonAsHighlighted) {
bg = colour(colours::primary).interpolatedWith(colour(colours::primaryHover), 0.6f);
} else if (button.getToggleState()) {
bg = colour(colours::primary);
} else {
bg = colour(colours::primary);
}

g.setColour(bg);
g.fillRoundedRectangle(bounds, cornerSize);
g.setColour(bg);
g.fillRoundedRectangle(bounds, cornerSize);
}

// Focus ring
drawFocusRing(g, button);
Expand All @@ -101,7 +131,14 @@ void AutoMixLookAndFeel::drawButtonText(juce::Graphics& g, juce::TextButton& but
auto font = getTextButtonFont(button, button.getHeight());
g.setFont(font);

auto textColour = button.isEnabled() ? colour(colours::text) : colour(colours::textDisabled);
const auto variant = variantOf(button);
juce::Colour textColour = colour(colours::text);
if (variant == buttonVariant::quiet)
textColour = colour(colours::primaryHover);
else if (variant == buttonVariant::danger)
textColour = colour(colours::error);
if (!button.isEnabled())
textColour = colour(colours::textDisabled);

g.setColour(textColour);

Expand Down Expand Up @@ -366,7 +403,9 @@ void AutoMixLookAndFeel::drawToggleButton(juce::Graphics& g, juce::ToggleButton&
// Fonts
// ─────────────────────────────────────────────────────────────────

juce::Font AutoMixLookAndFeel::getTextButtonFont(juce::TextButton& /*button*/, int /*buttonHeight*/) {
juce::Font AutoMixLookAndFeel::getTextButtonFont(juce::TextButton& button, int /*buttonHeight*/) {
if (static_cast<bool>(button.getProperties().getWithDefault("compact", false)))
return typography::caption();
return typography::body();
}

Expand Down
15 changes: 15 additions & 0 deletions src/app/style/AutoMixLookAndFeel.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@

namespace automix::app {

/// Button visual variants, selected via the "variant" component property.
/// No property (or an unknown value) means primary.
namespace buttonVariant {
inline const juce::Identifier key{"variant"};
inline constexpr const char* primary = "primary";
inline constexpr const char* secondary = "secondary";
inline constexpr const char* quiet = "quiet";
inline constexpr const char* danger = "danger";
} // namespace buttonVariant

inline void setButtonVariant(juce::Button& button, const char* variant) {
button.getProperties().set(buttonVariant::key, juce::String(variant));
button.repaint();
}

/// Custom LookAndFeel for AutoMixMaster.
/// Applies the dark audio-app theme defined in Theme.h to all standard JUCE widgets.
class AutoMixLookAndFeel final : public juce::LookAndFeel_V4 {
Expand Down
Loading
Loading