stella/src/gui/PopUpWidget.cxx

321 lines
9.1 KiB
C++

//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "FBSurface.hxx"
#include "Font.hxx"
#include "ContextMenu.hxx"
#include "DialogContainer.hxx"
#include "PopUpWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PopUpWidget::PopUpWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h, const VariantList& list,
const string& label, int labelWidth, int cmd)
: EditableWidget(boss, font, x, y - 1, w, h + 2),
_label(label),
_labelWidth(labelWidth)
{
_flags = Widget::FLAG_ENABLED | Widget::FLAG_RETAIN_FOCUS;
_bgcolor = kDlgColor;
_bgcolorhi = kDlgColor; // do not highlight the background
_textcolor = kTextColor;
_textcolorhi = kTextColor; // do not highlight the label
setEditable(false);
if(!_label.empty() && _labelWidth == 0)
_labelWidth = _font.getStringWidth(_label);
setArrow();
_w = w + _labelWidth + dropDownWidth(font); // 23
// vertically center the arrows and text
myTextY = (_h - _font.getFontHeight()) / 2;
myArrowsY = (_h - _arrowHeight) / 2;
myMenu = make_unique<ContextMenu>(this, font, list, cmd, w);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::setID(uInt32 id)
{
myMenu->setID(id);
Widget::setID(id);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::addItems(const VariantList& items)
{
myMenu->addItems(items);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::setSelected(const Variant& tag, const Variant& def)
{
myMenu->setSelected(tag, def);
setText(myMenu->getSelectedName());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::setSelectedIndex(int idx, bool changed)
{
_changed = changed;
myMenu->setSelectedIndex(idx);
setText(myMenu->getSelectedName());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::setSelectedMax(bool changed)
{
_changed = changed;
myMenu->setSelectedMax();
setText(myMenu->getSelectedName());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::clearSelection()
{
myMenu->clearSelection();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int PopUpWidget::getSelected() const
{
return myMenu->getSelected();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const string& PopUpWidget::getSelectedName() const
{
return myMenu->getSelectedName();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Variant& PopUpWidget::getSelectedTag() const
{
return myMenu->getSelectedTag();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
{
resetSelection();
if(!isEditable() || x > _w - dropDownWidth(_font))
{
if(isEnabled() && !myMenu->isVisible())
{
// Add menu just underneath parent widget
myMenu->show(getAbsX() + _labelWidth, getAbsY() + getHeight(),
dialog().surface().dstRect(), myMenu->getSelected());
}
}
else
{
x += _editScrollOffset - _labelWidth;
int width = 0;
uInt32 i;
for(i = 0; i < editString().size(); ++i)
{
width += _font.getCharWidth(editString()[i]);
if(width >= x)
break;
}
if(setCaretPos(i))
setDirty();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleMouseWheel(int x, int y, int direction)
{
if(isEnabled() && !myMenu->isVisible())
{
if(direction < 0)
myMenu->sendSelectionUp();
else
myMenu->sendSelectionDown();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleMouseEntered()
{
if(isEnabled())
setFlags(Widget::FLAG_HILITED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleMouseLeft()
{
if(isEnabled())
clearFlags(Widget::FLAG_HILITED);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PopUpWidget::handleEvent(Event::Type e)
{
if(!isEnabled())
return false;
switch(e)
{
case Event::UISelect:
handleMouseDown(0, 0, MouseButton::LEFT, 0);
return true;
case Event::UIUp:
case Event::UILeft:
case Event::UIPgUp:
return myMenu->sendSelectionUp();
case Event::UIDown:
case Event::UIRight:
case Event::UIPgDown:
return myMenu->sendSelectionDown();
case Event::UIHome:
return myMenu->sendSelectionFirst();
case Event::UIEnd:
return myMenu->sendSelectionLast();
default:
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
{
// Intercept all events sent through the PromptWidget
// They're likely from our ContextMenu, indicating a redraw is required
setText(myMenu->getSelectedName());
dialog().setDirty();
// Pass the cmd on to our parent
sendCommand(cmd, data, id);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::setArrow()
{
// Small down arrow
static constexpr std::array<uInt32, 7> down_arrow = {
0b100000001,
0b110000011,
0b111000111,
0b011101110,
0b001111100,
0b000111000,
0b000010000,
};
// Large down arrow
static constexpr std::array<uInt32, 10> down_arrow_large = {
0b1000000000001,
0b1100000000011,
0b1110000000111,
0b1111000001111,
0b0111100011110,
0b0011110111100,
0b0001111111000,
0b0000111110000,
0b0000011100000,
0b0000001000000
};
if(_font.getFontHeight() < 24)
{
_textOfs = 3;
_arrowWidth = 9;
_arrowHeight = 7;
_arrowImg = down_arrow.data();
}
else
{
_textOfs = 5;
_arrowWidth = 13;
_arrowHeight = 10;
_arrowImg = down_arrow_large.data();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::drawWidget(bool hilite)
{
//cerr << "PopUpWidget::drawWidget\n";
FBSurface& s = dialog().surface();
bool onTop = _boss->dialog().isOnTop();
int x = _x + _labelWidth;
int w = _w - _labelWidth;
// Draw the label, if any
if(_labelWidth > 0)
s.drawString(_font, _label, _x, _y + myTextY, _labelWidth,
isEnabled() && onTop ? _textcolor : kColor, TextAlign::Left);
// Draw a thin frame around us.
s.frameRect(x, _y, w, _h, isEnabled() && hilite ? kWidColorHi : kColor);
if(isEnabled() && hilite)
s.frameRect(x + w - (_arrowWidth * 2 - 1), _y, (_arrowWidth * 2 - 1), _h, kWidColorHi);
// Fill the background
ColorId bgCol = isEditable() ? kWidColor : kDlgColor;
s.fillRect(x + 1, _y + 1, w - (_arrowWidth * 2 - 1), _h - 2,
onTop ? _changed ? kDbgChangedColor : bgCol : kDlgColor);
s.fillRect(x + w - (_arrowWidth * 2 - 2), _y + 1, (_arrowWidth * 2 - 3), _h - 2,
onTop ? isEnabled() && hilite ? kBtnColorHi : bgCol : kBGColorLo);
// Draw an arrow pointing down at the right end to signal this is a dropdown/popup
s.drawBitmap(_arrowImg, x + w - (_arrowWidth * 1.5 - 1), _y + myArrowsY + 1,
!(isEnabled() && onTop) ? kColor : kTextColor, _arrowWidth, _arrowHeight);
// Draw the selected entry, if any
const string& name = editString();
bool editable = isEditable();
w -= dropDownWidth(_font);
TextAlign align = (_font.getStringWidth(name) > w && !editable) ?
TextAlign::Right : TextAlign::Left;
adjustOffset();
s.drawString(_font, name, x + _textOfs, _y + myTextY, w,
!(isEnabled() && onTop) ? kColor : _changed ? kDbgChangedTextColor : kTextColor,
align, editable ? -_editScrollOffset : 0, !editable);
if(editable)
drawCaretSelection();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Common::Rect PopUpWidget::getEditRect() const
{
return Common::Rect(_labelWidth + _textOfs, 1, _w - _textOfs - dropDownWidth(_font), _h);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::endEditMode()
{
// Editing is always enabled
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PopUpWidget::abortEditMode()
{
// Editing is always enabled
}