diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 8c8c963faba..6475a86227f 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -52,6 +52,7 @@ class InstrumentMidiIOView; class InstrumentMiscView; class Knob; class LcdSpinBox; +class LeftRightNav; class midiPortMenu; class DataFile; class PluginView; @@ -416,16 +417,19 @@ public slots: protected slots: void saveSettingsBtnClicked(); - + void viewNextInstrument(); + void viewPrevInstrument(); private: virtual void modelChanged(); + void viewInstrumentInDirection(int d); InstrumentTrack * m_track; InstrumentTrackView * m_itv; // widgets on the top of an instrument-track-window QLineEdit * m_nameLineEdit; + LeftRightNav * m_leftRightNav; Knob * m_volumeKnob; Knob * m_panningKnob; Knob * m_pitchKnob; diff --git a/include/LeftRightNav.h b/include/LeftRightNav.h new file mode 100644 index 00000000000..b675446ca1e --- /dev/null +++ b/include/LeftRightNav.h @@ -0,0 +1,49 @@ +/* + * LeftRightNav.cpp - side-by-side left-facing and right-facing arrows for navigation (looks like < > ) + * + * Copyright (c) 2015 Colin Wallace + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LEFT_RIGHT_NAV_H +#define LEFT_RIGHT_NAV_H + +#include "PixmapButton.h" + +#include + +class LeftRightNav : public QWidget +{ + Q_OBJECT +public: + LeftRightNav(QWidget *parent=NULL); + PixmapButton* getLeftBtn(); + PixmapButton* getRightBtn(); + void setShortcuts(const QKeySequence &leftShortcut=Qt::Key_Minus, const QKeySequence &rightShortcut=Qt::Key_Plus); +signals: + void onNavLeft(); + void onNavRight(); +private: + QHBoxLayout m_layout; + PixmapButton m_leftBtn; + PixmapButton m_rightBtn; +}; + +#endif \ No newline at end of file diff --git a/include/PixmapButton.h b/include/PixmapButton.h index 7e02c404719..04a7a7f5b64 100644 --- a/include/PixmapButton.h +++ b/include/PixmapButton.h @@ -42,6 +42,7 @@ class EXPORT PixmapButton : public AutomatableButton void setActiveGraphic( const QPixmap & _pm ); void setInactiveGraphic( const QPixmap & _pm, bool _update = true ); + QSize sizeHint() const; signals: void doubleClicked(); diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index b340325bd15..4544d26b33c 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -111,6 +111,7 @@ class TrackContainerView : public QWidget, public ModelView, void moveTrackView( TrackView * trackView, int indexTo ); void moveTrackViewUp( TrackView * trackView ); void moveTrackViewDown( TrackView * trackView ); + void scrollToTrackView( TrackView * _tv ); // -- for usage by trackView only --------------- TrackView * addTrackView( TrackView * _tv ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 548e4ad141a..a23c37f819d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -55,6 +55,7 @@ SET(LMMS_SRCS gui/widgets/InstrumentFunctionViews.cpp gui/widgets/InstrumentMidiIOView.cpp gui/widgets/InstrumentSoundShapingView.cpp + gui/widgets/LeftRightNav.cpp gui/widgets/Knob.cpp gui/widgets/LadspaControlView.cpp gui/widgets/LcdSpinBox.cpp diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index 8d8e3e1e8d6..29abbfc7cbb 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -22,6 +22,7 @@ * */ +#include #include #include @@ -200,6 +201,28 @@ void TrackContainerView::moveTrackViewDown( TrackView * trackView ) moveTrackView( trackView, index + 1 ); } +void TrackContainerView::scrollToTrackView( TrackView * _tv ) +{ + if (!m_trackViews.contains(_tv)) + { + qWarning("TrackContainerView::scrollToTrackView: TrackView is not owned by this"); + } + else + { + int currentScrollTop = m_scrollArea->verticalScrollBar()->value(); + int scrollAreaHeight = m_scrollArea->size().height(); + int trackViewTop = _tv->pos().y(); + int trackViewBottom = trackViewTop + _tv->size().height(); + + // displayed_location = widget_location - currentScrollTop + // want to make sure that the widget top has displayed location > 0, + // and widget bottom < scrollAreaHeight + // trackViewTop - scrollY > 0 && trackViewBottom - scrollY < scrollAreaHeight + // therefore scrollY < trackViewTop && scrollY > trackViewBottom - scrollAreaHeight + int newScroll = std::max( trackViewBottom-scrollAreaHeight, std::min(currentScrollTop, trackViewTop) ); + m_scrollArea->verticalScrollBar()->setValue(newScroll); + } +} diff --git a/src/gui/widgets/LeftRightNav.cpp b/src/gui/widgets/LeftRightNav.cpp new file mode 100644 index 00000000000..530d128d739 --- /dev/null +++ b/src/gui/widgets/LeftRightNav.cpp @@ -0,0 +1,91 @@ +/* + * LeftRightNav.cpp - side-by-side left-facing and right-facing arrows for navigation (looks like < > ) + * + * Copyright (c) 2015 Colin Wallace + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#include "LeftRightNav.h" +#include "ToolTip.h" +#include "embed.h" + + +LeftRightNav::LeftRightNav(QWidget *parent) + : QWidget(parent), + m_layout(this), + m_leftBtn(this, tr("Previous")), + m_rightBtn(this, tr("Next")) +{ + m_layout.setContentsMargins(0, 0, 0, 0); + m_layout.setSpacing(2); + + m_leftBtn.setCheckable(false); + m_rightBtn.setCheckable(false); + + m_leftBtn.setCursor(Qt::PointingHandCursor); + m_rightBtn.setCursor(Qt::PointingHandCursor); + + m_leftBtn.setActiveGraphic(embed::getIconPixmap( + "stepper-left-press")); + m_rightBtn.setActiveGraphic(embed::getIconPixmap( + "stepper-right-press" )); + + m_leftBtn.setInactiveGraphic(embed::getIconPixmap( + "stepper-left" )); + m_rightBtn.setInactiveGraphic(embed::getIconPixmap( + "stepper-right")); + + connect(&m_leftBtn, SIGNAL(clicked()), this, + SIGNAL(onNavLeft())); + connect(&m_rightBtn, SIGNAL(clicked()), this, + SIGNAL(onNavRight())); + + ToolTip::add(&m_leftBtn, tr("Previous")); + ToolTip::add(&m_rightBtn, tr("Next")); + + m_leftBtn.setWindowTitle(tr("Previous")); + m_rightBtn.setWindowTitle(tr("Next")); + + // AutomatableButton's right click menu (contains irrelevant options like copying and pasting values) + m_leftBtn.setContextMenuPolicy(Qt::NoContextMenu); + m_rightBtn.setContextMenuPolicy(Qt::NoContextMenu); + + m_layout.addWidget(&m_leftBtn); + m_layout.addWidget(&m_rightBtn); +} + +PixmapButton* LeftRightNav::getLeftBtn() +{ + return &m_leftBtn; +} +PixmapButton* LeftRightNav::getRightBtn() +{ + return &m_rightBtn; +} + +void LeftRightNav::setShortcuts(const QKeySequence &leftShortcut, const QKeySequence &rightShortcut) +{ + m_leftBtn.setShortcut(leftShortcut); + m_rightBtn.setShortcut(rightShortcut); + + ToolTip::add(&m_leftBtn, tr("Previous (%1)").arg(leftShortcut.toString())); + ToolTip::add(&m_rightBtn, tr("Next (%1)").arg(rightShortcut.toString())); +} \ No newline at end of file diff --git a/src/gui/widgets/PixmapButton.cpp b/src/gui/widgets/PixmapButton.cpp index e647c5e23dd..44e957eeffd 100644 --- a/src/gui/widgets/PixmapButton.cpp +++ b/src/gui/widgets/PixmapButton.cpp @@ -130,7 +130,17 @@ void PixmapButton::setInactiveGraphic( const QPixmap & _pm, bool _update ) } } - +QSize PixmapButton::sizeHint() const +{ + if( ( model() != NULL && model()->value() ) || m_pressed ) + { + return m_activePixmap.size(); + } + else + { + return m_inactivePixmap.size(); + } +} diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 7fcf2b0dc17..19d936a1f4d 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -66,6 +66,7 @@ #include "Knob.h" #include "LcdSpinBox.h" #include "LedCheckbox.h" +#include "LeftRightNav.h" #include "MainWindow.h" #include "MidiClient.h" #include "MidiPortMenu.h" @@ -79,6 +80,7 @@ #include "StringPairDrag.h" #include "TabWidget.h" #include "ToolTip.h" +#include "TrackContainerView.h" #include "TrackLabelButton.h" #include "ValueBuffer.h" #include "volume.h" @@ -1279,13 +1281,36 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : generalSettingsLayout->setContentsMargins( 8, 18, 8, 8 ); generalSettingsLayout->setSpacing( 6 ); + QWidget* nameAndChangeTrackWidget = new QWidget( generalSettingsWidget ); + QHBoxLayout* nameAndChangeTrackLayout = new QHBoxLayout( nameAndChangeTrackWidget ); + nameAndChangeTrackLayout->setContentsMargins( 0, 0, 0, 0 ); + nameAndChangeTrackLayout->setSpacing( 2 ); + // setup line edit for changing instrument track name m_nameLineEdit = new QLineEdit; m_nameLineEdit->setFont( pointSize<9>( m_nameLineEdit->font() ) ); connect( m_nameLineEdit, SIGNAL( textChanged( const QString & ) ), this, SLOT( textChanged( const QString & ) ) ); - generalSettingsLayout->addWidget( m_nameLineEdit ); + m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); + nameAndChangeTrackLayout->addWidget(m_nameLineEdit); + + + // set up left/right arrows for changing instrument + m_leftRightNav = new LeftRightNav(this); + connect( m_leftRightNav, SIGNAL( onNavLeft() ), this, + SLOT( viewPrevInstrument() ) ); + connect( m_leftRightNav, SIGNAL( onNavRight() ), this, + SLOT( viewNextInstrument() ) ); + m_leftRightNav->setWhatsThis( + tr( "Use these controls to view and edit the next/previous track in the song editor." ) ); + // m_leftRightNav->setShortcuts(); + nameAndChangeTrackLayout->addWidget(m_leftRightNav); + + + generalSettingsLayout->addWidget( nameAndChangeTrackWidget ); + + QHBoxLayout* basicControlsLayout = new QHBoxLayout; basicControlsLayout->setSpacing( 3 ); @@ -1692,3 +1717,61 @@ void InstrumentTrackWindow::loadSettings( const QDomElement& thisElement ) m_itv->m_tlb->setChecked( true ); } } + +void InstrumentTrackWindow::viewInstrumentInDirection(int d) +{ + // helper routine for viewNextInstrument, viewPrevInstrument + // d=-1 to view the previous instrument, + // d=+1 to view the next instrument + + const QList &trackViews = m_itv->trackContainerView()->trackViews(); + int idxOfMe = trackViews.indexOf(m_itv); + + // search for the next InstrumentTrackView (i.e. skip AutomationViews, etc) + // sometimes, the next InstrumentTrackView may already be open, in which case + // replace our window contents with the *next* closed Instrument Track and + // give focus to the InstrumentTrackView we skipped. + int idxOfNext = idxOfMe; + InstrumentTrackView *newView = nullptr; + InstrumentTrackView *bringToFront = nullptr; + do + { + idxOfNext = (idxOfNext + d + trackViews.size()) % trackViews.size(); + newView = dynamic_cast(trackViews[idxOfNext]); + // the window that should be brought to focus is the FIRST InstrumentTrackView that comes after us + if (bringToFront == nullptr && newView != nullptr) + { + bringToFront = newView; + } + // if the next instrument doesn't have an active window, then exit loop & load that one into our window. + if (newView != nullptr && !newView->m_tlb->isChecked()) + { + break; + } + } while (idxOfNext != idxOfMe); + + // avoid reloading the window if there is only one instrument, as that will just change the active tab + if (idxOfNext != idxOfMe) + { + // save current window pos and then hide the window by unchecking its button in the track list + QPoint curPos = parentWidget()->pos(); + m_itv->m_tlb->setChecked(false); + + // enable the new window by checking its track list button & moving it to where our window just was + newView->m_tlb->setChecked(true); + newView->getInstrumentTrackWindow()->parentWidget()->move(curPos); + + // scroll the SongEditor/BB-editor to make sure the new trackview label is visible + bringToFront->trackContainerView()->scrollToTrackView(bringToFront); + } + bringToFront->getInstrumentTrackWindow()->setFocus(); +} + +void InstrumentTrackWindow::viewNextInstrument() +{ + viewInstrumentInDirection(+1); +} +void InstrumentTrackWindow::viewPrevInstrument() +{ + viewInstrumentInDirection(-1); +} \ No newline at end of file