finalcut/src/fspinbox.cpp

448 lines
11 KiB
C++

/***********************************************************************
* fspinbox.cpp - Widget FSpinBox *
* *
* This file is part of the Final Cut widget toolkit *
* *
* Copyright 2019 Markus Gans *
* *
* The Final Cut is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public License *
* as published by the Free Software Foundation; either version 3 of *
* the License, or (at your option) any later version. *
* *
* The Final Cut 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this program. If not, see *
* <http://www.gnu.org/licenses/>. *
***********************************************************************/
#include <regex>
#include "final/fcolorpair.h"
#include "final/fevent.h"
#include "final/flabel.h"
#include "final/flineedit.h"
#include "final/fpoint.h"
#include "final/fsize.h"
#include "final/fspinbox.h"
#include "final/fstatusbar.h"
#include "final/fwidgetcolors.h"
namespace finalcut
{
//----------------------------------------------------------------------
// class FSpinBox
//----------------------------------------------------------------------
// constructors and destructor
//----------------------------------------------------------------------
FSpinBox::FSpinBox (FWidget* parent)
: FWidget(parent)
{
init();
}
//----------------------------------------------------------------------
FSpinBox::~FSpinBox() // destructor
{ }
// public methods of FSpinBox
//----------------------------------------------------------------------
void FSpinBox::setSize (const FSize& size, bool adjust)
{
FWidget::setSize (size, adjust);
FSize input_field_size(size);
input_field_size.scaleBy(-2, 0);
input_field.setSize (input_field_size, adjust);
}
//----------------------------------------------------------------------
void FSpinBox::setGeometry ( const FPoint& pos, const FSize& size
, bool adjust )
{
FWidget::setGeometry (pos, size, adjust);
FSize input_field_size(size);
input_field_size.scaleBy(-2, 0);
input_field.setGeometry (FPoint(1, 1), input_field_size, adjust);
}
//----------------------------------------------------------------------
bool FSpinBox::setEnable (bool enable)
{
FWidget::setEnable(enable);
input_field.setEnable(enable);
return enable;
}
//----------------------------------------------------------------------
bool FSpinBox::setFocus (bool enable)
{
FWidget::setFocus(enable);
input_field.setFocus(enable);
return enable;
}
//----------------------------------------------------------------------
bool FSpinBox::setShadow (bool enable)
{
if ( enable
&& getEncoding() != fc::VT100
&& getEncoding() != fc::ASCII )
{
setFlags().shadow = true;
setShadowSize(FSize(1, 1));
}
else
{
setFlags().shadow = false;
setShadowSize(FSize(0, 0));
}
return getFlags().shadow;
}
//----------------------------------------------------------------------
void FSpinBox::setValue (sInt64 n)
{
if ( n > max )
value = max;
else if ( n < min )
value = min;
else
value = n;
updateInputField();
}
//----------------------------------------------------------------------
void FSpinBox::setMinValue (sInt64 n)
{
if ( n <= max )
value = min = n;
}
//----------------------------------------------------------------------
void FSpinBox::setMaxValue (sInt64 n)
{
if ( n >= min )
max = n;
}
//----------------------------------------------------------------------
void FSpinBox::setRange (sInt64 m, sInt64 n)
{
if ( m <= n )
{
value = min = m;
max = n;
}
}
//----------------------------------------------------------------------
void FSpinBox::setPrefix (const FString& text)
{
pfix = text;
updateInputField();
}
//----------------------------------------------------------------------
void FSpinBox::setSuffix (const FString& text)
{
sfix = text;
updateInputField();
}
//----------------------------------------------------------------------
void FSpinBox::hide()
{
input_field.hide();
FWidget::hide();
FSize shadow = hasShadow() ? FSize(1, 1) : FSize(0, 0);
hideArea (getSize() + shadow);
}
//----------------------------------------------------------------------
void FSpinBox::onKeyPress (FKeyEvent* ev)
{
if ( ! isEnabled() )
return;
FKey key = ev->key();
switch ( key )
{
case fc::Fkey_tab:
focusNextChild();
break;
case fc::Fkey_btab:
focusPrevChild();
break;
case fc::Fkey_up:
increaseValue();
ev->accept();
break;
case fc::Fkey_down:
decreaseValue();
ev->accept();
break;
default:
break;
}
if ( ev->isAccepted() )
updateInputField();
}
//----------------------------------------------------------------------
void FSpinBox::onMouseDown (FMouseEvent* ev)
{
if ( ev->getButton() != fc::LeftButton
&& ev->getButton() != fc::MiddleButton )
return;
forceFocus();
if ( min == max )
return;
int mouse_x = ev->getX();
int mouse_y = ev->getY();
if ( mouse_x == int(getWidth()) - 1 && mouse_y == 1 )
{
spining_state = FSpinBox::spinDown;
decreaseValue();
updateInputField();
threshold_reached = false;
addTimer(threshold_time);
}
else if ( mouse_x == int(getWidth()) && mouse_y == 1 )
{
spining_state = FSpinBox::spinUp;
increaseValue();
updateInputField();
threshold_reached = false;
addTimer(threshold_time);
}
else
delOwnTimer();
}
//----------------------------------------------------------------------
void FSpinBox::onMouseUp (FMouseEvent*)
{
delOwnTimer();
spining_state = FSpinBox::noSpin;
}
//----------------------------------------------------------------------
void FSpinBox::onWheel (FWheelEvent* ev)
{
int wheel = ev->getWheel();
delOwnTimer();
forceFocus();
spining_state = FSpinBox::noSpin;
switch ( wheel )
{
case fc::WheelUp:
increaseValue();
updateInputField();
break;
case fc::WheelDown:
decreaseValue();
updateInputField();
break;
default:
break;
}
}
//----------------------------------------------------------------------
void FSpinBox::onTimer (FTimerEvent*)
{
if ( ! threshold_reached )
{
threshold_reached = true;
delOwnTimer();
addTimer(repeat_time);
}
switch ( spining_state )
{
case FSpinBox::spinUp:
increaseValue();
updateInputField();
break;
case FSpinBox::spinDown:
decreaseValue();
updateInputField();
break;
case FSpinBox::noSpin:
break;
}
}
// private methods of FSpinBox
//----------------------------------------------------------------------
void FSpinBox::init()
{
setShadow();
auto parent_widget = getParentWidget();
FLabel* label = input_field.getLabelObject();
label->setParent(getParent());
label->setForegroundColor (parent_widget->getForegroundColor());
label->setBackgroundColor (parent_widget->getBackgroundColor());
input_field.setLabelAssociatedWidget(this);
input_field.setInputFilter("[-[:digit:]]"); // Only numbers
input_field.unsetShadow();
input_field << value;
input_field.addCallback
(
"changed",
F_METHOD_CALLBACK (this, &FSpinBox::cb_inputFieldChange)
);
}
//----------------------------------------------------------------------
void FSpinBox::draw()
{
const auto& wc = getFWidgetColors();
FColorPair inc_button_color = [&] () -> FColorPair
{
if ( value == max )
return FColorPair ( wc.scrollbar_button_inactive_fg
, wc.scrollbar_button_inactive_bg );
else
return FColorPair ( wc.scrollbar_button_fg
, wc.scrollbar_button_bg );
}();
FColorPair dec_button_color = [&] () -> FColorPair
{
if ( value == min )
return FColorPair ( wc.scrollbar_button_inactive_fg
, wc.scrollbar_button_inactive_bg );
else
return FColorPair ( wc.scrollbar_button_fg
, wc.scrollbar_button_bg );
}();
print() << FPoint(int(getWidth()) - 1, 1)
<< dec_button_color
<< fc::BlackDownPointingTriangle // ▼
<< inc_button_color
<< fc::BlackUpPointingTriangle; // ▲
if ( getFlags().shadow )
drawShadow(this);
}
//----------------------------------------------------------------------
inline void FSpinBox::updateInputField()
{
input_field.clear();
input_field << pfix << value << sfix;
input_field.redraw();
redraw();
updateTerminal();
}
//----------------------------------------------------------------------
inline void FSpinBox::increaseValue()
{
if ( value < max )
{
value++;
processChanged();
}
else
delOwnTimer();
}
//----------------------------------------------------------------------
inline void FSpinBox::decreaseValue()
{
if ( value > min )
{
value--;
processChanged();
}
else
delOwnTimer();
}
//----------------------------------------------------------------------
void FSpinBox::processChanged()
{
emitCallback("changed");
}
//----------------------------------------------------------------------
void FSpinBox::forceFocus()
{
if ( hasFocus() )
return;
auto focused_widget = getFocusWidget();
setFocus();
if ( focused_widget )
focused_widget->redraw();
redraw();
if ( getStatusBar() )
getStatusBar()->drawMessage();
}
//----------------------------------------------------------------------
void FSpinBox::cb_inputFieldChange (finalcut::FWidget* w, FDataPtr)
{
auto lineedit = static_cast<FLineEdit*>(w);
if ( lineedit->getText().isEmpty() )
value = 0;
else
{
std::wregex regex(L"[-]?[[:xdigit:]]+");
std::wsmatch match;
std::wstring text = lineedit->getText().wc_str();
if ( std::regex_search(text, match, regex) )
{
FString tmp(match[0]);
value = tmp.toLong();
}
else
value = 0;
}
if ( value > max )
value = max;
else if ( value < min )
value = min;
updateInputField();
processChanged();
}
} // namespace finalcut