mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Merge branch 'main' of https://github.com/noctalia-dev/Nextalia
This commit is contained in:
@@ -410,6 +410,7 @@ add_executable(noctalia
|
||||
src/system/weather_service.cpp
|
||||
src/time/time_service.cpp
|
||||
src/ui/controls/box.cpp
|
||||
src/ui/controls/audio_spectrum.cpp
|
||||
src/ui/controls/button.cpp
|
||||
src/ui/controls/chip.cpp
|
||||
src/ui/controls/flex.cpp
|
||||
|
||||
@@ -216,9 +216,9 @@ gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug
|
||||
- [ ] Network
|
||||
- [ ] Bluetooth
|
||||
- [ ] Brightness
|
||||
- [ ] Microphone
|
||||
- [ ] Microphone <- unsure we need it
|
||||
- [ ] Power profile
|
||||
- [ ] System monitor
|
||||
- [~] System monitor <- need graph fix
|
||||
- [ ] Dock
|
||||
- [ ] Keyboard layout
|
||||
- [ ] Lock keys (Caps/Num)
|
||||
@@ -226,12 +226,11 @@ gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug
|
||||
- [ ] Night light button
|
||||
- [ ] Keep awake (idle inhibitor)
|
||||
- [ ] Audio visualizer
|
||||
- [ ] Launcher button
|
||||
- [ ] Control center button
|
||||
- [x] Launcher button
|
||||
- [ ] Session menu button
|
||||
- [ ] Settings button
|
||||
- [ ] Wallpaper selector button
|
||||
- [ ] Custom button (user-defined IPC)
|
||||
- [ ] Custom button (with user-defined IPC)
|
||||
- [ ] Settings button
|
||||
|
||||
### Desktop Widgets
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ Application::Application() : m_weatherService(m_configService, m_httpClient) {
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
m_wayland.setClipboardService(nullptr);
|
||||
|
||||
if (m_systemBus != nullptr) {
|
||||
m_systemBus->processPendingEvents();
|
||||
m_upowerService.reset();
|
||||
@@ -240,6 +242,7 @@ void Application::initUi() {
|
||||
|
||||
// Panel manager must be before bar so widgets can access PanelManager::instance()
|
||||
m_panelManager.initialize(m_wayland, &m_configService, &m_renderContext);
|
||||
m_configService.addReloadCallback([this]() { m_panelManager.close(); });
|
||||
m_panelManager.registerPanel("clipboard", std::make_unique<ClipboardPanel>(&m_clipboardService));
|
||||
m_panelManager.registerPanel("test", std::make_unique<TestPanel>());
|
||||
m_panelManager.registerPanel(
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "render/core/renderer.h"
|
||||
#include "shell/control_center/tab.h"
|
||||
#include "shell/panel/panel_manager.h"
|
||||
#include "ui/controls/audio_spectrum.h"
|
||||
#include "ui/controls/button.h"
|
||||
#include "ui/controls/flex.h"
|
||||
#include "ui/controls/image.h"
|
||||
@@ -32,8 +33,6 @@ namespace {
|
||||
const Logger kLog{"media_tab"};
|
||||
|
||||
constexpr float kArtworkSize = Style::controlHeightLg * 6;
|
||||
constexpr float kMediaColumnMinWidth = Style::controlHeightLg * 9;
|
||||
constexpr float kVisualizerColumnMinWidth = Style::controlHeightLg * 8;
|
||||
constexpr float kMediaNowCardMinHeight = Style::controlHeightLg * 11 + Style::spaceSm * 2;
|
||||
constexpr float kMediaControlsHeight = Style::controlHeightLg + Style::spaceXs;
|
||||
constexpr float kMediaPlayPauseHeight = Style::controlHeightLg + Style::spaceSm;
|
||||
@@ -184,7 +183,11 @@ ButtonVariant toggleVariant(bool active) { return active ? ButtonVariant::Accent
|
||||
} // namespace
|
||||
|
||||
MediaTab::MediaTab(MprisService* mpris, HttpClient* httpClient, PipeWireSpectrum* spectrum)
|
||||
: m_mpris(mpris), m_httpClient(httpClient), m_spectrum(spectrum) {}
|
||||
: m_mpris(mpris), m_httpClient(httpClient), m_spectrum(spectrum) {
|
||||
if (m_spectrum != nullptr) {
|
||||
m_spectrum->setBandCount(32);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Flex> MediaTab::build(Renderer& /*renderer*/) {
|
||||
const float scale = contentScale();
|
||||
@@ -200,7 +203,6 @@ std::unique_ptr<Flex> MediaTab::build(Renderer& /*renderer*/) {
|
||||
mediaColumn->setAlign(FlexAlign::Stretch);
|
||||
mediaColumn->setGap(Style::spaceSm * scale);
|
||||
mediaColumn->setFlexGrow(3.0f);
|
||||
mediaColumn->setMinWidth(kMediaColumnMinWidth * scale);
|
||||
m_mediaColumn = mediaColumn.get();
|
||||
|
||||
auto nowCard = std::make_unique<Flex>();
|
||||
@@ -209,7 +211,6 @@ std::unique_ptr<Flex> MediaTab::build(Renderer& /*renderer*/) {
|
||||
nowCard->setGap(Style::spaceMd * scale);
|
||||
nowCard->setFlexGrow(1.0f);
|
||||
nowCard->setMinHeight(kMediaNowCardMinHeight * scale);
|
||||
nowCard->setMinWidth(kMediaColumnMinWidth * scale);
|
||||
m_nowCard = nowCard.get();
|
||||
|
||||
auto mediaStack = std::make_unique<Flex>();
|
||||
@@ -402,32 +403,25 @@ std::unique_ptr<Flex> MediaTab::build(Renderer& /*renderer*/) {
|
||||
auto visualizerColumn = std::make_unique<Flex>();
|
||||
visualizerColumn->setDirection(FlexDirection::Vertical);
|
||||
visualizerColumn->setAlign(FlexAlign::Stretch);
|
||||
visualizerColumn->setGap(Style::spaceSm * scale);
|
||||
visualizerColumn->setGap(0.0f);
|
||||
visualizerColumn->setFlexGrow(2.0f);
|
||||
visualizerColumn->setMinWidth(kVisualizerColumnMinWidth * scale);
|
||||
visualizerColumn->setPadding(Style::spaceSm * scale);
|
||||
visualizerColumn->setBackground(palette.surfaceVariant);
|
||||
visualizerColumn->setRadius(Style::radiusLg * scale);
|
||||
visualizerColumn->setBorderWidth(0.0f);
|
||||
visualizerColumn->setSoftness(1.0f);
|
||||
visualizerColumn->setClipChildren(true);
|
||||
m_visualizerColumn = visualizerColumn.get();
|
||||
|
||||
auto visualizerCard = std::make_unique<Flex>();
|
||||
applyCard(*visualizerCard, scale);
|
||||
visualizerCard->setAlign(FlexAlign::Stretch);
|
||||
visualizerCard->setJustify(FlexJustify::Center);
|
||||
visualizerCard->setFlexGrow(1.0f);
|
||||
m_visualizerCard = visualizerCard.get();
|
||||
|
||||
auto visualizerTitle = std::make_unique<Label>();
|
||||
visualizerTitle->setText("Audio Visualizer");
|
||||
visualizerTitle->setBold(true);
|
||||
visualizerTitle->setFontSize(Style::fontSizeTitle * scale);
|
||||
visualizerTitle->setColor(palette.onSurface);
|
||||
visualizerCard->addChild(std::move(visualizerTitle));
|
||||
|
||||
auto visualizerBody = std::make_unique<Label>();
|
||||
visualizerBody->setText("Reserved space for the future visualizer.");
|
||||
visualizerBody->setFontSize(Style::fontSizeBody * scale);
|
||||
visualizerBody->setColor(palette.onSurfaceVariant);
|
||||
visualizerCard->addChild(std::move(visualizerBody));
|
||||
|
||||
visualizerColumn->addChild(std::move(visualizerCard));
|
||||
auto visualizerSpectrum = std::make_unique<AudioSpectrum>();
|
||||
visualizerSpectrum->setGradient(palette.secondary, palette.tertiary);
|
||||
visualizerSpectrum->setSpacingRatio(0.5f);
|
||||
visualizerSpectrum->setOrientation(AudioSpectrumOrientation::Vertical);
|
||||
visualizerSpectrum->setMirrored(true);
|
||||
visualizerSpectrum->setCentered(true);
|
||||
visualizerSpectrum->setFlexGrow(1.0f);
|
||||
m_visualizerSpectrum = visualizerSpectrum.get();
|
||||
visualizerColumn->addChild(std::move(visualizerSpectrum));
|
||||
tab->addChild(std::move(mediaColumn));
|
||||
tab->addChild(std::move(visualizerColumn));
|
||||
return tab;
|
||||
@@ -444,7 +438,7 @@ void MediaTab::layout(Renderer& renderer, float contentWidth, float bodyHeight)
|
||||
|
||||
const float cardInnerWidth =
|
||||
std::max(0.0f, m_nowCard->width() - (m_nowCard->paddingLeft() + m_nowCard->paddingRight()));
|
||||
const float mediaWidth = std::clamp(cardInnerWidth, kMediaColumnMinWidth * scale, Style::controlHeightLg * 11.0f * scale);
|
||||
const float mediaWidth = std::clamp(cardInnerWidth, 1.0f, Style::controlHeightLg * 11.0f * scale);
|
||||
m_mediaStack->setSize(mediaWidth, 0.0f);
|
||||
|
||||
if (m_playerSelect != nullptr) {
|
||||
@@ -512,6 +506,15 @@ void MediaTab::layout(Renderer& renderer, float contentWidth, float bodyHeight)
|
||||
if (m_progressSlider != nullptr) {
|
||||
m_progressSlider->setSize(mediaWidth, 0.0f);
|
||||
}
|
||||
if (m_visualizerColumn != nullptr && m_visualizerSpectrum != nullptr) {
|
||||
const float innerWidth =
|
||||
std::max(0.0f, m_visualizerColumn->width() - (m_visualizerColumn->paddingLeft() + m_visualizerColumn->paddingRight()));
|
||||
const float innerHeight =
|
||||
std::max(0.0f, m_visualizerColumn->height() - (m_visualizerColumn->paddingTop() + m_visualizerColumn->paddingBottom()));
|
||||
m_visualizerSpectrum->setPosition(0.0f, 0.0f);
|
||||
m_visualizerSpectrum->setSize(innerWidth, innerHeight);
|
||||
m_visualizerSpectrum->layout(renderer);
|
||||
}
|
||||
|
||||
m_rootLayout->layout(renderer);
|
||||
}
|
||||
@@ -520,6 +523,9 @@ void MediaTab::update(Renderer& renderer) {
|
||||
if (!m_active) {
|
||||
return;
|
||||
}
|
||||
if (m_visualizerSpectrum != nullptr && m_spectrum != nullptr) {
|
||||
m_visualizerSpectrum->setValues(m_spectrum->values());
|
||||
}
|
||||
refresh(renderer);
|
||||
}
|
||||
|
||||
@@ -538,10 +544,10 @@ void MediaTab::onClose() {
|
||||
m_rootLayout = nullptr;
|
||||
m_mediaColumn = nullptr;
|
||||
m_visualizerColumn = nullptr;
|
||||
m_visualizerSpectrum = nullptr;
|
||||
m_artwork = nullptr;
|
||||
m_nowCard = nullptr;
|
||||
m_mediaStack = nullptr;
|
||||
m_visualizerCard = nullptr;
|
||||
m_trackTitle = nullptr;
|
||||
m_trackArtist = nullptr;
|
||||
m_trackAlbum = nullptr;
|
||||
|
||||
@@ -16,6 +16,7 @@ class MprisService;
|
||||
class PipeWireSpectrum;
|
||||
class Select;
|
||||
class Slider;
|
||||
class AudioSpectrum;
|
||||
|
||||
class MediaTab : public Tab {
|
||||
public:
|
||||
@@ -39,10 +40,10 @@ private:
|
||||
Flex* m_rootLayout = nullptr;
|
||||
Flex* m_mediaColumn = nullptr;
|
||||
Flex* m_visualizerColumn = nullptr;
|
||||
AudioSpectrum* m_visualizerSpectrum = nullptr;
|
||||
Image* m_artwork = nullptr;
|
||||
Flex* m_nowCard = nullptr;
|
||||
Flex* m_mediaStack = nullptr;
|
||||
Flex* m_visualizerCard = nullptr;
|
||||
Label* m_trackTitle = nullptr;
|
||||
Label* m_trackArtist = nullptr;
|
||||
Label* m_trackAlbum = nullptr;
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
#include "ui/controls/audio_spectrum.h"
|
||||
|
||||
#include "render/core/color.h"
|
||||
#include "ui/controls/box.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
AudioSpectrum::AudioSpectrum() {
|
||||
m_lowColor = hex("#ebbcba");
|
||||
m_highColor = hex("#9ccfd8");
|
||||
}
|
||||
|
||||
void AudioSpectrum::setValues(const std::vector<float>& values) {
|
||||
m_values = values;
|
||||
ensureBarCount(m_mirrored ? m_values.size() * 2 : m_values.size());
|
||||
markDirty();
|
||||
}
|
||||
|
||||
void AudioSpectrum::setGradient(const Color& lowColor, const Color& highColor) {
|
||||
m_lowColor = lowColor;
|
||||
m_highColor = highColor;
|
||||
recolorBars();
|
||||
}
|
||||
|
||||
void AudioSpectrum::setSpacingRatio(float ratio) {
|
||||
const float clamped = std::max(0.0f, ratio);
|
||||
if (m_spacingRatio == clamped) {
|
||||
return;
|
||||
}
|
||||
m_spacingRatio = clamped;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
void AudioSpectrum::setOrientation(AudioSpectrumOrientation orientation) {
|
||||
if (m_orientation == orientation) {
|
||||
return;
|
||||
}
|
||||
m_orientation = orientation;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
void AudioSpectrum::setMirrored(bool mirrored) {
|
||||
if (m_mirrored == mirrored) {
|
||||
return;
|
||||
}
|
||||
m_mirrored = mirrored;
|
||||
ensureBarCount(m_mirrored ? m_values.size() * 2 : m_values.size());
|
||||
markDirty();
|
||||
}
|
||||
|
||||
void AudioSpectrum::setCentered(bool centered) {
|
||||
if (m_centered == centered) {
|
||||
return;
|
||||
}
|
||||
m_centered = centered;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
void AudioSpectrum::layout(Renderer& /*renderer*/) {
|
||||
const int barCount = static_cast<int>(m_bars.size());
|
||||
if (barCount <= 0 || width() <= 0.0f || height() <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float slotUnits = static_cast<float>(barCount) + m_spacingRatio * static_cast<float>(std::max(0, barCount - 1));
|
||||
if (m_orientation == AudioSpectrumOrientation::Horizontal) {
|
||||
const float barWidth = std::max(1.0f, std::floor(width() / std::max(1.0f, slotUnits)));
|
||||
const float gap = std::floor(barWidth * m_spacingRatio);
|
||||
const float usedWidth =
|
||||
barWidth * static_cast<float>(barCount) + gap * static_cast<float>(std::max(0, barCount - 1));
|
||||
float x = std::floor(std::max(0.0f, (width() - usedWidth) * 0.5f));
|
||||
|
||||
for (int i = 0; i < barCount; ++i) {
|
||||
const int valueIndex =
|
||||
m_mirrored ? (i < static_cast<int>(m_values.size()) ? static_cast<int>(m_values.size()) - 1 - i
|
||||
: i - static_cast<int>(m_values.size()))
|
||||
: i;
|
||||
const float value =
|
||||
valueIndex >= 0 && valueIndex < static_cast<int>(m_values.size())
|
||||
? std::clamp(m_values[static_cast<std::size_t>(valueIndex)], 0.0f, 1.0f)
|
||||
: 0.0f;
|
||||
const float barHeight = std::floor(value * height() + 0.5f);
|
||||
const float y = m_centered ? std::floor((height() - barHeight) * 0.5f) : std::floor(height() - barHeight);
|
||||
if (auto* bar = m_bars[static_cast<std::size_t>(i)]; bar != nullptr) {
|
||||
bar->setPosition(x, y);
|
||||
bar->setSize(barWidth, barHeight);
|
||||
}
|
||||
x += barWidth + gap;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const float barHeight = std::max(1.0f, std::floor(height() / std::max(1.0f, slotUnits)));
|
||||
const float gap = std::floor(barHeight * m_spacingRatio);
|
||||
const float usedHeight =
|
||||
barHeight * static_cast<float>(barCount) + gap * static_cast<float>(std::max(0, barCount - 1));
|
||||
float y = std::floor(std::max(0.0f, (height() - usedHeight) * 0.5f));
|
||||
|
||||
for (int i = 0; i < barCount; ++i) {
|
||||
const int valueIndex =
|
||||
m_mirrored ? (i < static_cast<int>(m_values.size()) ? static_cast<int>(m_values.size()) - 1 - i
|
||||
: i - static_cast<int>(m_values.size()))
|
||||
: i;
|
||||
const float value =
|
||||
valueIndex >= 0 && valueIndex < static_cast<int>(m_values.size())
|
||||
? std::clamp(m_values[static_cast<std::size_t>(valueIndex)], 0.0f, 1.0f)
|
||||
: 0.0f;
|
||||
const float barWidth = std::floor(value * width() + 0.5f);
|
||||
const float x = m_centered ? std::floor((width() - barWidth) * 0.5f) : std::floor(width() - barWidth);
|
||||
if (auto* bar = m_bars[static_cast<std::size_t>(i)]; bar != nullptr) {
|
||||
bar->setPosition(x, y);
|
||||
bar->setSize(barWidth, barHeight);
|
||||
}
|
||||
y += barHeight + gap;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSpectrum::ensureBarCount(std::size_t count) {
|
||||
while (m_bars.size() < count) {
|
||||
auto bar = std::make_unique<Box>();
|
||||
bar->setBorder(rgba(0, 0, 0, 0), 0.0f);
|
||||
bar->setRadius(0.0f);
|
||||
bar->setSoftness(0.0f);
|
||||
m_bars.push_back(static_cast<Box*>(addChild(std::move(bar))));
|
||||
}
|
||||
|
||||
while (m_bars.size() > count) {
|
||||
removeChild(m_bars.back());
|
||||
m_bars.pop_back();
|
||||
}
|
||||
|
||||
recolorBars();
|
||||
}
|
||||
|
||||
void AudioSpectrum::recolorBars() {
|
||||
const std::size_t lastIndex = m_bars.empty() ? 0 : m_bars.size() - 1;
|
||||
for (std::size_t i = 0; i < m_bars.size(); ++i) {
|
||||
const float t = lastIndex == 0 ? 0.0f : static_cast<float>(i) / static_cast<float>(lastIndex);
|
||||
if (auto* bar = m_bars[i]; bar != nullptr) {
|
||||
bar->setFill(lerpColor(m_lowColor, m_highColor, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "render/core/color.h"
|
||||
#include "render/scene/node.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
class Box;
|
||||
class Renderer;
|
||||
|
||||
enum class AudioSpectrumOrientation : std::uint8_t {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
};
|
||||
|
||||
class AudioSpectrum : public Node {
|
||||
public:
|
||||
AudioSpectrum();
|
||||
|
||||
void setValues(const std::vector<float>& values);
|
||||
void setGradient(const Color& lowColor, const Color& highColor);
|
||||
void setSpacingRatio(float ratio);
|
||||
void setOrientation(AudioSpectrumOrientation orientation);
|
||||
void setMirrored(bool mirrored);
|
||||
void setCentered(bool centered);
|
||||
|
||||
void layout(Renderer& renderer) override;
|
||||
|
||||
private:
|
||||
void ensureBarCount(std::size_t count);
|
||||
void recolorBars();
|
||||
|
||||
std::vector<float> m_values;
|
||||
std::vector<Box*> m_bars;
|
||||
Color m_lowColor = {};
|
||||
Color m_highColor = {};
|
||||
float m_spacingRatio = 0.5f;
|
||||
AudioSpectrumOrientation m_orientation = AudioSpectrumOrientation::Horizontal;
|
||||
bool m_mirrored = false;
|
||||
bool m_centered = false;
|
||||
};
|
||||
Reference in New Issue
Block a user