scummvm/gui/widgets/popup.cpp
aryanrawlani28 4b6976c558 GUI: U32: Reduce number of files changed and fixes
Up until last commit, everything was working fine but the amount of files changed was too large. This commit tries to reduce the changes.

- Add a fake constructor to Keymap, text-to-speech, setDescription (save-state)
- Redirecting functions for PopUpWidget::appendEntry, ButtonWidget::setLabel, GUIErrorMessage
- Use the above functions and constructors to reduce changes in Engines
- Fix warnings being in unicode. Only output english text in - Warnings, Errors, etc.
- Mark some strings as "translation" strings. (Not yet added to POTFILES)
- Remove some CP related things from po/modules.mk
- Previously used some Common::convertToU32 where it was not necessary, replace this with u32constructor
2020-08-30 14:43:41 +02:00

549 lines
13 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/system.h"
#include "gui/gui-manager.h"
#include "gui/widgets/popup.h"
#include "gui/ThemeEval.h"
namespace GUI {
//
// PopUpDialog
//
PopUpDialog::PopUpDialog(Widget *boss, const Common::String &name, int clickX, int clickY):
Dialog(name),
_boss(boss),
// Remember original mouse position
_clickX(clickX),
_clickY(clickY),
_selection(-1),
_initialSelection(-1),
_openTime(0),
_twoColumns(false),
_entriesPerColumn(1),
_leftPadding(0),
_rightPadding(0),
_lineHeight(kLineHeight),
_lastRead(-1) {
_backgroundType = ThemeEngine::kDialogBackgroundNone;
_w = _boss->getWidth();
}
void PopUpDialog::open() {
// Time the popup was opened
_openTime = g_system->getMillis();
_initialSelection = _selection;
// Calculate real popup dimensions
_h = _entries.size() * _lineHeight + 2;
_entriesPerColumn = 1;
// Perform clipping / switch to scrolling mode if we don't fit on the screen
// FIXME - OSystem should send out notification messages when the screen
// resolution changes... we could generalize CommandReceiver and CommandSender.
const int screenH = g_system->getOverlayHeight();
// HACK: For now, we do not do scrolling. Instead, we draw the dialog
// in two columns if it's too tall.
if (_h >= screenH) {
const int screenW = g_system->getOverlayWidth();
_twoColumns = true;
_entriesPerColumn = _entries.size() / 2;
if (_entries.size() & 1)
_entriesPerColumn++;
_h = _entriesPerColumn * _lineHeight + 2;
_w = 0;
for (uint i = 0; i < _entries.size(); i++) {
int width = g_gui.getStringWidth(_entries[i]);
if (width > _w)
_w = width;
}
_w = 2 * _w + 10;
if (!(_w & 1))
_w++;
if (_selection >= _entriesPerColumn) {
_x -= _w / 2;
_y = _boss->getAbsY() - (_selection - _entriesPerColumn) * _lineHeight;
}
if (_w >= screenW)
_w = screenW - 1;
if (_x < 0)
_x = 0;
if (_x + _w >= screenW)
_x = screenW - 1 - _w;
} else
_twoColumns = false;
if (_h >= screenH)
_h = screenH - 1;
if (_y < 0)
_y = 0;
else if (_y + _h >= screenH)
_y = screenH - 1 - _h;
// TODO - implement scrolling if we had to move the menu, or if there are too many entries
_lastRead = -1;
Dialog::open();
}
void PopUpDialog::reflowLayout() {
}
void PopUpDialog::drawDialog(DrawLayer layerToDraw) {
Dialog::drawDialog(layerToDraw);
if (g_gui.useRTL()) {
_x = g_system->getOverlayWidth() - _x - _w + g_gui.getOverlayOffset();
}
// Draw the menu border
g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), ThemeEngine::kWidgetBackgroundPlain);
/*if (_twoColumns)
g_gui.vLine(_x + _w / 2, _y, _y + _h - 2, g_gui._color);*/
// Draw the entries
int count = _entries.size();
for (int i = 0; i < count; i++) {
drawMenuEntry(i, i == _selection);
}
// The last entry may be empty. Fill it with black.
/*if (_twoColumns && (count & 1)) {
g_gui.fillRect(_x + 1 + _w / 2, _y + 1 + kLineHeight * (_entriesPerColumn - 1), _w / 2 - 1, kLineHeight, g_gui._bgcolor);
}*/
}
void PopUpDialog::handleMouseUp(int x, int y, int button, int clickCount) {
int absX = x + getAbsX();
int absY = y + getAbsY();
// Mouse was released. If it wasn't moved much since the original mouse down,
// let the popup stay open. If it did move, assume the user made his selection.
int dist = (_clickX - absX) * (_clickX - absX) + (_clickY - absY) * (_clickY - absY);
if (dist > 3 * 3 || g_system->getMillis() - _openTime > 300) {
int item = findItem(x, y);
setResult(item);
close();
}
_clickX = -1;
_clickY = -1;
_openTime = (uint32)-1;
}
void PopUpDialog::handleMouseWheel(int x, int y, int direction) {
if (direction < 0)
moveUp();
else if (direction > 0)
moveDown();
}
void PopUpDialog::handleMouseMoved(int x, int y, int button) {
// Compute over which item the mouse is...
int item = findItem(x, y);
if (item >= 0 && _entries[item].size() == 0)
item = -1;
if (item == -1 && !isMouseDown()) {
setSelection(_initialSelection);
return;
}
// ...and update the selection accordingly
setSelection(item);
if (_lastRead != item && _entries.size() > 0 && item != -1) {
read(_entries[item]);
_lastRead = item;
}
}
void PopUpDialog::handleMouseLeft(int button) {
_lastRead = -1;
}
void PopUpDialog::read(const Common::U32String &str) {
#ifdef USE_TTS
if (ConfMan.hasKey("tts_enabled", "scummvm") &&
ConfMan.getBool("tts_enabled", "scummvm")) {
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
if (ttsMan != nullptr)
ttsMan->say(str);
}
#endif
}
void PopUpDialog::handleKeyDown(Common::KeyState state) {
if (state.keycode == Common::KEYCODE_ESCAPE) {
// Don't change the previous selection
setResult(-1);
close();
return;
}
if (isMouseDown())
return;
switch (state.keycode) {
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
setResult(_selection);
close();
break;
// Keypad & special keys
// - if num lock is set, we ignore the keypress
// - if num lock is not set, we fall down to the special key case
case Common::KEYCODE_KP1:
if (state.flags & Common::KBD_NUM)
break;
// fall through
case Common::KEYCODE_END:
setSelection(_entries.size()-1);
break;
case Common::KEYCODE_KP2:
if (state.flags & Common::KBD_NUM)
break;
// fall through
case Common::KEYCODE_DOWN:
moveDown();
break;
case Common::KEYCODE_KP7:
if (state.flags & Common::KBD_NUM)
break;
// fall through
case Common::KEYCODE_HOME:
setSelection(0);
break;
case Common::KEYCODE_KP8:
if (state.flags & Common::KBD_NUM)
break;
// fall through
case Common::KEYCODE_UP:
moveUp();
break;
default:
break;
}
}
void PopUpDialog::setPosition(int x, int y) {
_x = x;
_y = y;
}
void PopUpDialog::setPadding(int left, int right) {
_leftPadding = left;
_rightPadding = right;
}
void PopUpDialog::setLineHeight(int lineHeight) {
_lineHeight = lineHeight;
}
void PopUpDialog::setWidth(uint16 width) {
_w = width;
}
void PopUpDialog::appendEntry(const Common::U32String &entry) {
_entries.push_back(entry);
}
void PopUpDialog::clearEntries() {
_entries.clear();
}
int PopUpDialog::findItem(int x, int y) const {
if (x >= 0 && x < _w && y >= 0 && y < _h) {
if (_twoColumns) {
uint entry = (y - 2) / _lineHeight;
if (x > _w / 2) {
entry += _entriesPerColumn;
if (entry >= _entries.size())
return -1;
}
return entry;
}
return (y - 2) / _lineHeight;
}
return -1;
}
void PopUpDialog::setSelection(int item) {
if (item != _selection) {
// Undraw old selection
if (_selection >= 0)
drawMenuEntry(_selection, false);
// Change selection
_selection = item;
// Draw new selection
if (item >= 0)
drawMenuEntry(item, true);
}
}
bool PopUpDialog::isMouseDown() {
// TODO/FIXME - need a way to determine whether any mouse buttons are pressed or not.
// Sure, we could just count mouse button up/down events, but that is cumbersome and
// error prone. Would be much nicer to add an API to OSystem for this...
return false;
}
void PopUpDialog::moveUp() {
if (_selection < 0) {
setSelection(_entries.size() - 1);
} else if (_selection > 0) {
int item = _selection;
do {
item--;
} while (item >= 0 && _entries[item].size() == 0);
if (item >= 0)
setSelection(item);
}
}
void PopUpDialog::moveDown() {
int lastItem = _entries.size() - 1;
if (_selection < 0) {
setSelection(0);
} else if (_selection < lastItem) {
int item = _selection;
do {
item++;
} while (item <= lastItem && _entries[item].size() == 0);
if (item <= lastItem)
setSelection(item);
}
}
void PopUpDialog::drawMenuEntry(int entry, bool hilite) {
// Draw one entry of the popup menu, including selection
assert(entry >= 0);
int x, y, w;
if (_twoColumns) {
int n = _entries.size() / 2;
if (_entries.size() & 1)
n++;
if (entry >= n) {
x = _x + 1 + _w / 2;
y = _y + 1 + _lineHeight * (entry - n);
} else {
x = _x + 1;
y = _y + 1 + _lineHeight * entry;
}
w = _w / 2 - 1;
} else {
x = _x + 1;
y = _y + 1 + _lineHeight * entry;
w = _w - 2;
}
Common::U32String &name(_entries[entry]);
Common::Rect r1(x, y, x + w, y + _lineHeight);
Common::Rect r2(x + 1, y + 2, x + w, y + 2 + _lineHeight);
Graphics::TextAlign alignment = Graphics::kTextAlignLeft;
int pad = _leftPadding;
if (g_gui.useRTL()) {
if (_twoColumns) {
r1.translate(this->getWidth() - w, 0); // Shift the line-separator to the "first" col of RTL popup
}
r2.left = g_system->getOverlayWidth() - r2.left - w + g_gui.getOverlayOffset();
r2.right = r2.left + w;
alignment = Graphics::kTextAlignRight;
pad = _rightPadding;
}
if (name.size() == 0) {
// Draw a separator
g_gui.theme()->drawLineSeparator(r1);
} else {
g_gui.theme()->drawText(
r2,
name, hilite ? ThemeEngine::kStateHighlight : ThemeEngine::kStateEnabled,
alignment, ThemeEngine::kTextInversionNone, pad
);
}
}
#pragma mark -
//
// PopUpWidget
//
PopUpWidget::PopUpWidget(GuiObject *boss, const String &name, const U32String &tooltip)
: Widget(boss, name, tooltip), CommandSender(boss) {
setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_IGNORE_DRAG);
_type = kPopUpWidget;
_selectedItem = -1;
_leftPadding = _rightPadding = 0;
}
PopUpWidget::PopUpWidget(GuiObject *boss, int x, int y, int w, int h, const U32String &tooltip)
: Widget(boss, x, y, w, h, tooltip), CommandSender(boss) {
setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_IGNORE_DRAG);
_type = kPopUpWidget;
_selectedItem = -1;
_leftPadding = _rightPadding = 0;
}
void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount) {
if (isEnabled()) {
PopUpDialog popupDialog(this, "", x + getAbsX(), y + getAbsY());
popupDialog.setPosition(getAbsX(), getAbsY() - _selectedItem * kLineHeight);
popupDialog.setPadding(_leftPadding, _rightPadding);
popupDialog.setWidth(getWidth() - kLineHeight + 2);
for (uint i = 0; i < _entries.size(); i++) {
popupDialog.appendEntry(_entries[i].name);
}
popupDialog.setSelection(_selectedItem);
int newSel = popupDialog.runModal();
if (newSel != -1 && _selectedItem != newSel) {
_selectedItem = newSel;
sendCommand(kPopUpItemSelectedCmd, _entries[_selectedItem].tag);
markAsDirty();
}
}
}
void PopUpWidget::handleMouseWheel(int x, int y, int direction) {
if (isEnabled()) {
int newSelection = _selectedItem + direction;
// Skip separator entries
while ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
_entries[newSelection].name.equals("")) {
newSelection += direction;
}
// Just update the selected item when we're in range
if ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
(newSelection != _selectedItem)) {
_selectedItem = newSelection;
sendCommand(kPopUpItemSelectedCmd, _entries[_selectedItem].tag);
markAsDirty();
}
}
}
void PopUpWidget::reflowLayout() {
_leftPadding = g_gui.xmlEval()->getVar("Globals.PopUpWidget.Padding.Left", 0);
_rightPadding = g_gui.xmlEval()->getVar("Globals.PopUpWidget.Padding.Right", 0);
Widget::reflowLayout();
}
void PopUpWidget::appendEntry(const U32String &entry, uint32 tag) {
Entry e;
e.name = entry;
e.tag = tag;
_entries.push_back(e);
}
void PopUpWidget::appendEntry(const String &entry, uint32 tag) {
appendEntry(U32String(entry), tag);
}
void PopUpWidget::clearEntries() {
_entries.clear();
_selectedItem = -1;
}
void PopUpWidget::setSelected(int item) {
if (item != _selectedItem) {
if (item >= 0 && item < (int)_entries.size()) {
_selectedItem = item;
} else {
_selectedItem = -1;
}
}
}
void PopUpWidget::setSelectedTag(uint32 tag) {
uint item;
for (item = 0; item < _entries.size(); ++item) {
if (_entries[item].tag == tag) {
setSelected(item);
return;
}
}
}
void PopUpWidget::drawWidget() {
Common::U32String sel;
if (_selectedItem >= 0)
sel = _entries[_selectedItem].name;
int pad = _leftPadding;
if (g_gui.useRTL() && _useRTL)
pad = _rightPadding;
g_gui.theme()->drawPopUpWidget(Common::Rect(_x, _y, _x + _w, _y + _h), sel, pad, _state, (g_gui.useRTL() && _useRTL));
}
} // End of namespace GUI