Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
ControlPanel.cpp
1 #include <Gui/Widgets/ControlPanel.hpp>
2 
3 #include <Gui/Widgets/MatrixEditor.hpp>
4 
5 #include <PowerSlider/PowerSlider.hpp>
6 
7 #include <QCheckBox>
8 #include <QColorDialog>
9 #include <QComboBox>
10 #include <QDialog>
11 #include <QFileDialog>
12 #include <QLabel>
13 #include <QPlainTextEdit>
14 #include <QPushButton>
15 #include <QSlider>
16 #include <QVBoxLayout>
17 
18 namespace Ra::Gui::Widgets {
19 
20 ControlPanel::ControlPanel( const std::string& name, bool hline, QWidget* parent ) :
21  QFrame( parent ) {
22  setObjectName( name.c_str() );
23 
24  m_currentLayout = new QVBoxLayout( this );
25  setLayout( m_currentLayout );
26  if ( hline ) {
27  auto panelName = new QLabel( this );
28  if ( !name.empty() ) {
29  panelName->setText( name.c_str() );
30  // panelName->setStyleSheet("border-bottom-width: 1px; border-bottom-style: solid;
31  // border-radius: 0px;");
32  panelName->setFrameStyle( QFrame::Box );
33  }
34  else { panelName->setFrameStyle( QFrame::HLine ); }
35  m_currentLayout->addWidget( panelName );
36  }
37  setVisible( false );
38  // setFrameStyle( QFrame::Panel );
39  m_layouts.push( m_currentLayout );
40 }
41 
42 // Method to populate the panel
43 void ControlPanel::addOption( const std::string& name,
44  std::function<void( bool )> callback,
45  bool set,
46  const std::string& tooltip ) {
47  auto button = new QCheckBox( name.c_str(), this );
48  button->setLayoutDirection( Qt::RightToLeft );
49  button->setAutoExclusive( false );
50  button->setChecked( set );
51  if ( !tooltip.empty() ) {
52  button->setToolTip(
53  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
54  }
55  m_currentLayout->addWidget( button );
56  connect( button, &QCheckBox::stateChanged, std::move( callback ) );
57 }
58 
59 void ControlPanel::addLabel( const std::string& text ) {
60  auto label = new QLabel( text.c_str(), nullptr );
61  m_currentLayout->addWidget( label );
62 }
63 
64 void ControlPanel::addButton( const std::string& name,
65  std::function<void()> callback,
66  const std::string& tooltip ) {
67  auto button = new QPushButton( name.c_str(), nullptr );
68  if ( !tooltip.empty() ) {
69  button->setToolTip(
70  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
71  }
72  m_currentLayout->addWidget( button );
73  connect( button, &QPushButton::clicked, std::move( callback ) );
74 }
75 
76 void ControlPanel::addSliderInput( const std::string& name,
77  std::function<void( int )> callback,
78  int initial,
79  int min,
80  int max,
81  const std::string& tooltip ) {
82  auto inputLayout = new QHBoxLayout();
83  auto inputLabel = new QLabel( tr( name.c_str() ), this );
84  inputLayout->addWidget( inputLabel );
85  inputLayout->addStretch();
86  auto sliderLayout = new QHBoxLayout();
87  auto inputField = new QSlider( Qt::Horizontal, this );
88  if ( !tooltip.empty() ) {
89  inputLabel->setToolTip(
90  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
91  }
92  auto spinbox = new QSpinBox( this );
93  inputField->setRange( min, max );
94  inputField->setValue( initial );
95  inputField->setTickPosition( QSlider::TicksAbove );
96  inputField->setTickInterval( 1 );
97  inputField->setFocusPolicy( Qt::StrongFocus );
98  spinbox->setRange( min, max );
99  spinbox->setValue( initial );
100  connect( inputField, &QSlider::valueChanged, spinbox, &QSpinBox::setValue );
101  // Qt6 do not need QOverload<int> but Qt5 do. Keep Qt5 syntax
102  connect(
103  spinbox, QOverload<int>::of( &QSpinBox::valueChanged ), inputField, &QSlider::setValue );
104  connect( inputField, &QSlider::valueChanged, std::move( callback ) );
105  sliderLayout->addWidget( inputField );
106  sliderLayout->addWidget( spinbox );
107  inputLayout->addLayout( sliderLayout );
108  m_currentLayout->addLayout( inputLayout );
109 }
110 
111 void ControlPanel::addPowerSliderInput( const std::string& name,
112  std::function<void( double )> callback,
113  double initial,
114  double min,
115  double max,
116  const std::string& tooltip ) {
117  auto inputLayout = new QHBoxLayout();
118  auto inputLabel = new QLabel( tr( name.c_str() ), this );
119  auto inputField = new PowerSlider();
120  if ( !tooltip.empty() ) {
121  inputLabel->setToolTip(
122  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
123  }
124  inputField->setObjectName( name.c_str() );
125  inputField->setRange( min, max );
126  inputField->setValue( initial );
127  inputField->setSingleStep( 0.01 );
128  inputLayout->addWidget( inputLabel );
129  inputLayout->addStretch();
130  inputLayout->addWidget( inputField );
131  connect( inputField, &PowerSlider::valueChanged, std::move( callback ) );
132  m_currentLayout->addLayout( inputLayout );
133 }
134 
135 void ControlPanel::addMatrixInput( const std::string& name,
136  std::function<void( const Ra::Core::MatrixN& )> callback,
137  const Ra::Core::MatrixN& initial,
138  int dec,
139  const std::string& tooltip ) {
140  auto inputLayout = new QHBoxLayout();
141 
142  auto inputLabel = new QLabel( tr( name.c_str() ), this );
143  auto inputField = new MatrixEditor( initial, dec, this );
144 
145  if ( !tooltip.empty() ) {
146  inputLabel->setToolTip(
147  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
148  }
149  inputLayout->addWidget( inputLabel );
150  inputLayout->addWidget( inputField );
151  connect( inputField, &MatrixEditor::valueChanged, std::move( callback ) );
152  m_currentLayout->addLayout( inputLayout );
153 }
154 
156  const std::string& name,
157  const std::function<void( const Ra::Core::Utils::Color& clr )>& callback,
159  bool withAlpha,
160  const std::string& tooltip ) {
161  auto button = new QPushButton( name.c_str(), this );
162  auto srgbColor = Ra::Core::Utils::Color::linearRGBTosRGB( color );
163  auto clrBttn = QColor::fromRgbF( srgbColor[0], srgbColor[1], srgbColor[2], srgbColor[3] );
164  auto clrDlg = [callback, clrBttn, withAlpha, button, name]() mutable {
165  clrBttn = QColorDialog::getColor(
166  clrBttn,
167  nullptr,
168  name.c_str(),
169  ( withAlpha ? QColorDialog::ShowAlphaChannel : QColorDialog::ColorDialogOptions() )
170 #ifndef OS_MACOS
171  | QColorDialog::DontUseNativeDialog
172 #endif
173  );
174  if ( clrBttn.isValid() ) {
175  // update the background color of the viewer
176  auto lum = 0.2126_ra * Scalar( clrBttn.redF() ) +
177  0.7151_ra * Scalar( clrBttn.greenF() ) +
178  0.0721_ra * Scalar( clrBttn.blueF() );
180  Ra::Core::Utils::Color( Scalar( clrBttn.redF() ),
181  Scalar( clrBttn.greenF() ),
182  Scalar( clrBttn.blueF() ),
183  Scalar( clrBttn.alphaF() ) ) );
184  callback( bgk );
185  QString qss = QString( "background-color: %1" ).arg( clrBttn.name() );
186  if ( lum > 1_ra / 3_ra ) { qss += QString( "; color: #000000" ); }
187  else { qss += QString( "; color: #FFFFFF" ); }
188  button->setStyleSheet( qss );
189  }
190  };
191 
192  if ( !tooltip.empty() ) {
193  button->setToolTip(
194  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
195  }
196  // for contrast computation : see
197  // https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html
198  auto lum = 0.2126_ra * srgbColor[0] + 0.7151_ra * srgbColor[1] + 0.0721_ra * srgbColor[2];
199  QString qss = QString( "background-color: %1" ).arg( clrBttn.name() );
200  if ( lum > 1_ra / 3_ra ) { qss += QString( "; color: #000000" ); }
201  else { qss += QString( "; color: #FFFFFF" ); }
202  button->setStyleSheet( qss );
203 
204  connect( button, &QPushButton::clicked, clrDlg );
205  m_currentLayout->addWidget( button );
206 }
207 
208 void ControlPanel::addFileInput( const std::string& name,
209  std::function<void( std::string )> callback,
210  const std::string& rootDirectory,
211  const std::string& filters,
212  const std::string& tooltip ) {
213 
214  auto openFile = [this, callback, rootDirectory, filters]() {
215  auto fltrs = QString::fromStdString( filters );
216  auto pathList = QFileDialog::getOpenFileNames(
217  this, "Open files", QString::fromStdString( rootDirectory ), fltrs );
218  if ( !pathList.empty() ) {
219  std::string fileList;
220  for ( const auto& file : pathList ) {
221  fileList += file.toStdString() + ";";
222  }
223  fileList.erase( fileList.size() - 1 );
224  callback( fileList );
225  }
226  else { callback( "" ); }
227  };
228 
229  addButton( name, openFile, tooltip );
230 }
231 
232 void ControlPanel::addFileOutput( const std::string& name,
233  std::function<void( std::string )> callback,
234  const std::string& rootDirectory,
235  const std::string& filters,
236  const std::string& tooltip ) {
237 
238  auto saveFile = [this, callback, rootDirectory, filters]() {
239  auto fltrs = QString::fromStdString( filters );
240  auto filename = QFileDialog::getSaveFileName(
241  this, "Save file", QString::fromStdString( rootDirectory ), fltrs );
242  if ( !filename.isEmpty() ) { callback( filename.toStdString() ); }
243  };
244  addButton( name, saveFile, tooltip );
245 }
246 
247 void ControlPanel::beginLayout( QBoxLayout::Direction dir ) {
248  m_layouts.push( m_currentLayout );
249  m_currentLayout = new QBoxLayout( dir );
250 }
251 
252 void ControlPanel::endLayout( bool separator ) {
253  m_layouts.top()->addLayout( m_currentLayout );
254  m_currentLayout = m_layouts.top();
255  if ( separator ) { addSeparator(); }
256  m_layouts.pop();
257 }
258 
260  QFrame* line = new QFrame();
261  switch ( m_currentLayout->direction() ) {
262  case QBoxLayout::LeftToRight:
263  case QBoxLayout::RightToLeft:
264  line->setFrameShape( QFrame::VLine );
265  break;
266  case QBoxLayout::TopToBottom:
267  case QBoxLayout::BottomToTop:
268  line->setFrameShape( QFrame::HLine );
269  break;
270  default:
271  line->setFrameShape( QFrame::HLine );
272  }
273  line->setFrameShadow( QFrame::Sunken );
274  m_currentLayout->addWidget( line );
275 }
276 
277 void ControlPanel::addComboBox( const std::string& name,
278  std::function<void( int )> callback,
279  int initial,
280  const std::vector<std::string>& items,
281  const std::string& tooltip ) {
282  auto inputLayout = new QHBoxLayout();
283  auto inputLabel = new QLabel( tr( name.c_str() ), this );
284  auto inputField = new QComboBox( this );
285  for ( auto v : items ) {
286  inputField->addItem( QString::fromStdString( v ) );
287  }
288  if ( !tooltip.empty() ) {
289  inputLabel->setToolTip(
290  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
291  }
292  inputField->setCurrentIndex( initial );
293  inputLayout->addWidget( inputLabel );
294  inputLayout->addWidget( inputField );
295  connect(
296  inputField, QOverload<int>::of( &QComboBox::currentIndexChanged ), std::move( callback ) );
297  m_currentLayout->addLayout( inputLayout );
298 }
299 
300 void ControlPanel::addComboBox( const std::string& name,
301  std::function<void( const QString& )> callback,
302  const std::string& initial,
303  const std::vector<std::string>& items,
304  const std::string& tooltip ) {
305  auto inputLayout = new QHBoxLayout();
306  auto inputLabel = new QLabel( tr( name.c_str() ), this );
307  auto inputField = new QComboBox( this );
308  for ( auto v : items ) {
309  inputField->addItem( QString::fromStdString( v ) );
310  }
311  if ( !tooltip.empty() ) {
312  inputLabel->setToolTip(
313  QString( "<qt>%1</qt>" ).arg( QString( tooltip.c_str() ).toHtmlEscaped() ) );
314  }
315  inputField->setCurrentText( QString::fromStdString( initial ) );
316  inputLayout->addWidget( inputLabel );
317  inputLayout->addWidget( inputField );
318  connect( inputField,
319  QOverload<const QString&>::of( &QComboBox::currentTextChanged ),
320  std::move( callback ) );
321  m_currentLayout->addLayout( inputLayout );
322 }
323 
324 void ControlPanel::addStretch( int stretch ) {
325  m_currentLayout->addStretch( stretch );
326 }
327 
328 void ControlPanel::addWidget( QWidget* newWidget ) {
329  m_currentLayout->addWidget( newWidget );
330 }
331 
332 } // namespace Ra::Gui::Widgets
static ColorBase sRGBToLinearRGB(const ColorBase &srgb)
convert the color expressed in sRGB color space to linear RGB
Definition: Color.hpp:62
static ColorBase linearRGBTosRGB(const ColorBase &lrgb)
convert the color expressed in linear RGB color space to sRGB
Definition: Color.hpp:72
ControlPanel(const std::string &name, bool hline=true, QWidget *parent=nullptr)
void addColorInput(const std::string &name, const std::function< void(const Ra::Core::Utils::Color &clr)> &callback, Ra::Core::Utils::Color color=Ra::Core::Utils::Color::Black(), bool withAlpha=true, const std::string &tooltip="")
void addButton(const std::string &name, std::function< void()> callback, const std::string &tooltip="")
void addLabel(const std::string &text)
void addComboBox(const std::string &name, std::function< void(int)> callback, int initial, const std::vector< std::string > &items, const std::string &tooltip="")
void addSliderInput(const std::string &name, std::function< void(int)> callback, int initial=0, int min=0, int max=100, const std::string &tooltip="")
void addPowerSliderInput(const std::string &name, std::function< void(double)> callback, double initial=0, double min=0, double max=100, const std::string &tooltip="")
void addMatrixInput(const std::string &name, std::function< void(const Ra::Core::MatrixN &)> callback, const Ra::Core::MatrixN &initial, int dec=3, const std::string &tooltip="")
void addOption(const std::string &name, std::function< void(bool)> callback, bool set=false, const std::string &tooltip="")
void addFileInput(const std::string &name, std::function< void(std::string)> callback, const std::string &rootDirectory, const std::string &filters, const std::string &tooltip="")
void beginLayout(QBoxLayout::Direction dir=QBoxLayout::LeftToRight)
void endLayout(bool separator=false)
void addStretch(int stretch=0)
void addFileOutput(const std::string &name, std::function< void(std::string)> callback, const std::string &rootDirectory, const std::string &filters, const std::string &tooltip="")
void addWidget(QWidget *newWidget)