From e776bc768cf9afca1867200e25d64d315cd72a3e Mon Sep 17 00:00:00 2001 From: Calvin Morrison Date: Fri, 15 May 2026 10:10:04 -0400 Subject: Full mixer implementation — tray, popup, prefs, devices tab with port indicators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the complete tmix feature set built since the initial skeleton: balance knob, level meters, KLed mute button, system tray with scroll-wheel volume and recording indicator, tray popup, preferences dialog, right-click context menus, single-instance enforcement, scroll area, window geometry persistence, and Devices tab with PA card profile switcher and live port availability indicators (2-column layout, in-place updates on plug/unplug). Co-Authored-By: Claude Sonnet 4.6 --- src/ui/tmixpopup.cpp | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/ui/tmixpopup.cpp (limited to 'src/ui/tmixpopup.cpp') diff --git a/src/ui/tmixpopup.cpp b/src/ui/tmixpopup.cpp new file mode 100644 index 0000000..068c66e --- /dev/null +++ b/src/ui/tmixpopup.cpp @@ -0,0 +1,134 @@ +#include "tmixpopup.h" +#include "devicewidget.h" +#include "../model/audiodevice.h" +#include "../model/pulsemodel.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TmixPopup::TmixPopup( PulseModel *model, TQWidget *parent ) + : TQWidget( parent, "TmixPopup", + WStyle_Customize | WType_Popup | WStyle_DialogBorder ), + m_model(model), m_devWidget(0) +{ + // Single frame fills the popup — header and content share the same border + TQVBoxLayout *outer = new TQVBoxLayout( this, 0, 0 ); + + TQFrame *frame = new TQFrame( this ); + frame->setFrameStyle( TQFrame::NoFrame ); + outer->addWidget( frame ); + + m_layout = new TQVBoxLayout( frame, 0, 0 ); + + // Thin title strip — inside the frame, so edges line up exactly + TQLabel *header = new TQLabel( i18n("Volume"), frame ); + header->setAlignment( TQt::AlignCenter ); + header->setFixedHeight( 23 ); + header->setPaletteBackgroundColor( palette().active().mid() ); + header->setPaletteForegroundColor( palette().active().text() ); + m_layout->addWidget( header ); + + // Spacer between header and device widget + m_layout->addSpacing( 4 ); + + // device widget inserted at index 0 by setDevice() + + m_layout->addSpacing( 4 ); + + // "Mixer" button — flat, compact + TQPushButton *btn = new TQPushButton( i18n("Mixer"), frame ); + btn->setFlat( true ); + btn->setFixedWidth( 70 ); + m_layout->addWidget( btn, 0, TQt::AlignHCenter ); + m_layout->addSpacing( 4 ); + connect( btn, TQ_SIGNAL(clicked()), this, TQ_SIGNAL(showMixerRequested()) ); + + connect( model, TQ_SIGNAL(defaultOutputChanged(AudioDevice*)), + this, TQ_SLOT(onDefaultOutputChanged(AudioDevice*)) ); + + setDevice( model->defaultOutput() ); +} + +void TmixPopup::setDevice( AudioDevice *dev ) +{ + if ( m_devWidget ) { + m_layout->remove( m_devWidget ); + delete m_devWidget; + m_devWidget = 0; + } + if ( !dev ) return; + + m_devWidget = new DeviceWidget( dev, m_model, + static_cast( m_layout->parent() ) ); + m_devWidget->setSeparatorVisible( false ); + m_layout->insertWidget( 2, m_devWidget ); + m_devWidget->show(); + adjustSize(); +} + +void TmixPopup::onDefaultOutputChanged( AudioDevice *dev ) +{ + setDevice( dev ); +} + +void TmixPopup::toggleAt( const TQPoint &trayPos, const TQSize &traySize ) +{ + if ( isVisible() ) { + hide(); + return; + } + + adjustSize(); + + TQRect screen = TQApplication::desktop()->screenGeometry( trayPos ); + + int x = trayPos.x() + traySize.width() / 2 - width() / 2; + int y = trayPos.y() - height(); + + if ( y < screen.top() ) + y = trayPos.y() + traySize.height(); + + if ( x + width() > screen.right() ) + x = screen.right() - width(); + if ( x < screen.left() ) + x = screen.left(); + + move( x, y ); + show(); + + KWin::setState( winId(), NET::KeepAbove | NET::SkipTaskbar | NET::SkipPager ); + KWin::setOnAllDesktops( winId(), true ); +} + +bool TmixPopup::justHidden() const +{ + return m_hideTime.isValid() && m_hideTime.elapsed() < 300; +} + +void TmixPopup::hideEvent( TQHideEvent *e ) +{ + TQWidget::hideEvent( e ); + m_hideTime.start(); +} + +void TmixPopup::wheelEvent( TQWheelEvent *e ) +{ + AudioDevice *dev = m_model->defaultOutput(); + if ( !dev ) return; + TDEConfig *cfg = TDEGlobal::config(); + cfg->setGroup("General"); + int step = cfg->readNumEntry("ScrollStep", 5); + int delta = e->delta() > 0 ? step : -step; + int vol = TQMAX( 0, TQMIN( 100, dev->volume() + delta ) ); + dev->setVolume( vol ); +} + +#include "tmixpopup.moc" -- cgit v1.2.3