mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 05:38:56 +00:00
669f86705d
that should fix redrawing bugs with the about dialog. Also I displayed cached background usage in the console for now, since it produces redraw bugs with the old theme (maybe someone with knowledge how redrawing of that dialog is handled should look at that). svn-id: r20337
551 lines
15 KiB
C++
551 lines
15 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2006 The ScummVM project
|
|
*
|
|
* 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.
|
|
*
|
|
* $Header$
|
|
*/
|
|
|
|
#include "gui/theme.h"
|
|
|
|
namespace GUI {
|
|
ThemeClassic::ThemeClassic(OSystem *system) : Theme() {
|
|
_system = system;
|
|
_initOk = false;
|
|
memset(&_screen, 0, sizeof(_screen));
|
|
#ifdef OLDGUI_TRANSPARENCY
|
|
memset(&_dialog, 0, sizeof(_dialog));
|
|
#endif
|
|
_font = 0;
|
|
|
|
// Maybe change this filename
|
|
_configFile.loadFromFile("classic.ini");
|
|
}
|
|
|
|
ThemeClassic::~ThemeClassic() {
|
|
deinit();
|
|
}
|
|
|
|
bool ThemeClassic::init() {
|
|
deinit();
|
|
_screen.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor));
|
|
if (_screen.pixels) {
|
|
_initOk = true;
|
|
clearAll();
|
|
_bgcolor = _system->RGBToColor(0, 0, 0);
|
|
_color = _system->RGBToColor(104, 104, 104);
|
|
_shadowcolor = _system->RGBToColor(64, 64, 64);
|
|
_textcolor = _system->RGBToColor(32, 160, 32);
|
|
_textcolorhi = _system->RGBToColor(0, 255, 0);
|
|
if (_screen.w >= 400 && _screen.h >= 300) {
|
|
_font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
|
|
} else {
|
|
_font = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ThemeClassic::deinit() {
|
|
if (_initOk) {
|
|
_system->hideOverlay();
|
|
_screen.free();
|
|
_initOk = false;
|
|
}
|
|
}
|
|
|
|
void ThemeClassic::refresh() {
|
|
init();
|
|
_bgcolor = _system->RGBToColor(0, 0, 0);
|
|
_color = _system->RGBToColor(104, 104, 104);
|
|
_shadowcolor = _system->RGBToColor(64, 64, 64);
|
|
_textcolor = _system->RGBToColor(32, 160, 32);
|
|
_textcolorhi = _system->RGBToColor(0, 255, 0);
|
|
_system->showOverlay();
|
|
}
|
|
|
|
void ThemeClassic::enable() {
|
|
init();
|
|
resetDrawArea();
|
|
_system->showOverlay();
|
|
clearAll();
|
|
}
|
|
|
|
void ThemeClassic::disable() {
|
|
_system->hideOverlay();
|
|
}
|
|
|
|
void ThemeClassic::openDialog() {
|
|
#ifdef OLDGUI_TRANSPARENCY
|
|
if (!_dialog) {
|
|
_dialog = new DialogState;
|
|
assert(_dialog);
|
|
// first dialog
|
|
_dialog->screen.create(_screen.w, _screen.h, sizeof(OverlayColor));
|
|
}
|
|
memcpy(_dialog->screen.pixels, _screen.pixels, _screen.pitch*_screen.h);
|
|
blendScreenToDialog();
|
|
#endif
|
|
}
|
|
|
|
void ThemeClassic::closeDialog() {
|
|
#ifdef OLDGUI_TRANSPARENCY
|
|
if (_dialog) {
|
|
_dialog->screen.free();
|
|
delete _dialog;
|
|
_dialog = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ThemeClassic::clearAll() {
|
|
if (!_initOk)
|
|
return;
|
|
_system->clearOverlay();
|
|
// FIXME: problem with the 'pitch'
|
|
_system->grabOverlay((OverlayColor*)_screen.pixels, _screen.w);
|
|
}
|
|
|
|
void ThemeClassic::drawAll() {
|
|
if (!_initOk)
|
|
return;
|
|
}
|
|
|
|
void ThemeClassic::resetDrawArea() {
|
|
if (_initOk) {
|
|
_drawArea = Common::Rect(0, 0, _screen.w, _screen.h);
|
|
}
|
|
}
|
|
|
|
void ThemeClassic::drawDialogBackground(const Common::Rect &r, uint16 hints, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
|
|
restoreBackground(r);
|
|
|
|
if ((hints & THEME_HINT_SAVE_BACKGROUND) && !(hints & THEME_HINT_FIRST_DRAW)) {
|
|
addDirtyRect(r);
|
|
return;
|
|
}
|
|
|
|
box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor);
|
|
addDirtyRect(r, (hints & THEME_HINT_SAVE_BACKGROUND) != 0);
|
|
}
|
|
|
|
void ThemeClassic::drawText(const Common::Rect &r, const Common::String &str, kState state, kTextAlign align, bool inverted, int deltax, bool useEllipsis) {
|
|
if (!_initOk)
|
|
return;
|
|
|
|
if (!inverted) {
|
|
restoreBackground(r);
|
|
_font->drawString(&_screen, str, r.left, r.top, r.width(), getColor(state), convertAligment(align), deltax, useEllipsis);
|
|
} else {
|
|
_screen.fillRect(r, getColor(state));
|
|
_font->drawString(&_screen, str, r.left, r.top, r.width(), _bgcolor, convertAligment(align), deltax, useEllipsis);
|
|
}
|
|
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawChar(const Common::Rect &r, byte ch, const Graphics::Font *font, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
restoreBackground(r);
|
|
font->drawChar(&_screen, ch, r.left, r.top, getColor(state));
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawWidgetBackground(const Common::Rect &r, uint16 hints, kWidgetBackground background, kState state) {
|
|
if (!_initOk || background == kWidgetBackgroundNo)
|
|
return;
|
|
|
|
switch (background) {
|
|
case kWidgetBackgroundBorder:
|
|
restoreBackground(r);
|
|
box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor);
|
|
break;
|
|
|
|
case kWidgetBackgroundBorderSmall:
|
|
restoreBackground(r);
|
|
box(r.left, r.top, r.width(), r.height());
|
|
break;
|
|
|
|
case kWidgetBackgroundPlain:
|
|
restoreBackground(r);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
};
|
|
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawButton(const Common::Rect &r, const Common::String &str, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
restoreBackground(r);
|
|
|
|
drawWidgetBackground(r, 0, kWidgetBackgroundBorder, state);
|
|
|
|
const int off = (r.height() - _font->getFontHeight()) / 2;
|
|
_font->drawString(&_screen, str, r.left, r.top+off, r.width(), getColor(state), Graphics::kTextAlignCenter, 0, false);
|
|
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawSurface(const Common::Rect &r, const Graphics::Surface &surface, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
|
|
Common::Rect rect(r.left, r.top, r.left + surface.w, r.top + surface.h);
|
|
rect.clip(_screen.w, _screen.h);
|
|
|
|
if (!rect.isValidRect())
|
|
return;
|
|
|
|
assert(surface.bytesPerPixel == sizeof(OverlayColor));
|
|
|
|
OverlayColor *src = (OverlayColor *)surface.pixels;
|
|
OverlayColor *dst = (OverlayColor *)_screen.getBasePtr(rect.left, rect.top);
|
|
|
|
int w = rect.width();
|
|
int h = rect.height();
|
|
|
|
while (h--) {
|
|
memcpy(dst, src, surface.pitch);
|
|
src += w;
|
|
// FIXME: this should be pitch
|
|
dst += _screen.w;
|
|
}
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawSlider(const Common::Rect &r, int width, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
Common::Rect r2 = r;
|
|
|
|
restoreBackground(r);
|
|
|
|
box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor);
|
|
r2.left = r.left + 2;
|
|
r2.top = r.top + 2;
|
|
r2.bottom = r.bottom - 2;
|
|
r2.right = r2.left + width;
|
|
if (r2.right > r.right - 2) {
|
|
r2.right = r.right - 2;
|
|
}
|
|
|
|
_screen.fillRect(r2, getColor(state));
|
|
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawCheckbox(const Common::Rect &r, const Common::String &str, bool checked, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
|
|
Common::Rect r2 = r;
|
|
int checkBoxSize = getFontHeight();
|
|
if (checkBoxSize > r.height()) {
|
|
checkBoxSize = r.height();
|
|
}
|
|
r2.bottom = r2.top + checkBoxSize;
|
|
|
|
restoreBackground(r2);
|
|
|
|
box(r.left, r.top, checkBoxSize, checkBoxSize, _color, _shadowcolor);
|
|
|
|
if (checked) {
|
|
r2.top += 3;
|
|
r2.bottom = r.top + checkBoxSize - 4;
|
|
r2.left += 3;
|
|
r2.right = r.left + checkBoxSize - 4;
|
|
|
|
OverlayColor c = getColor(state);
|
|
|
|
// Draw a cross
|
|
_screen.drawLine(r2.left, r2.top, r2.right, r2.bottom, c);
|
|
_screen.drawLine(r2.left, r2.bottom, r2.right, r2.top, c);
|
|
|
|
if (r2.height() > 5) {
|
|
// Thicken the lines
|
|
_screen.drawLine(r2.left, r2.top + 1, r2.right - 1, r2.bottom, c);
|
|
_screen.drawLine(r2.left + 1, r2.top, r2.right, r2.bottom - 1, c);
|
|
_screen.drawLine(r2.left, r2.bottom - 1, r2.right - 1, r2.top, c);
|
|
_screen.drawLine(r2.left + 1, r2.bottom, r2.right, r2.top + 1, c);
|
|
}
|
|
|
|
r2 = r;
|
|
}
|
|
|
|
r2.left += checkBoxSize + 10;
|
|
_font->drawString(&_screen, str, r2.left, r2.top, r2.width(), getColor(state), Graphics::kTextAlignCenter, 0, true);
|
|
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawTab(const Common::Rect &r, const Common::String &str, bool active, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
restoreBackground(r);
|
|
box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor);
|
|
_font->drawString(&_screen, str, r.left, r.top+2, r.width(), getColor(state), Graphics::kTextAlignCenter, 0, true);
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawScrollbar(const Common::Rect &r, int sliderY, int sliderHeight, kScrollbarState scroll, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
restoreBackground(r);
|
|
Common::Rect r2 = r;
|
|
box(r.left, r.top, r.width(), r.height(), _color, _shadowcolor);
|
|
|
|
const int UP_DOWN_BOX_HEIGHT = r.width() + 1;
|
|
const int B = 3;
|
|
const int arrowSize = (r.width() / 2 - B + 1);
|
|
|
|
OverlayColor color = 0;
|
|
if (scroll == kScrollbarStateSinglePage) {
|
|
color = _color;
|
|
} else if (scroll == kScrollbarStateUp && state == kStateHighlight) {
|
|
color = _textcolorhi;
|
|
} else {
|
|
color = _textcolor;
|
|
}
|
|
|
|
// draws the 'up' button
|
|
box(r.left, r.top, r.width(), UP_DOWN_BOX_HEIGHT, _color, _shadowcolor);
|
|
Common::Point p0 = Common::Point(r.left + r.width() / 2, r.top + (UP_DOWN_BOX_HEIGHT - arrowSize - 1) / 2);
|
|
Common::Point p1 = Common::Point(p0.x - arrowSize, p0.y + arrowSize);
|
|
Common::Point p2 = Common::Point(p0.x + arrowSize, p0.y + arrowSize);
|
|
for (; p1.x <= p2.x; ++p1.x)
|
|
_screen.drawLine(p0.x, p0.y, p1.x, p1.y, color);
|
|
|
|
if (scroll != kScrollbarStateSinglePage) {
|
|
r2.top += sliderY;
|
|
r2.left += 2;
|
|
r2.right -= 2;
|
|
r2.bottom = r2.top + sliderHeight;
|
|
_screen.fillRect(r2, (state == kStateHighlight && scroll == kScrollbarStateSlider) ? _textcolorhi : _textcolor);
|
|
box(r2.left, r2.top, r2.width(), r2.height());
|
|
int y = r2.top + sliderHeight / 2;
|
|
color = (state == kStateHighlight && scroll == kScrollbarStateSlider) ? _color : _bgcolor;
|
|
_screen.hLine(r2.left + 1, y - 2, r2.right - 2, color);
|
|
_screen.hLine(r2.left + 1, y, r2.right - 2, color);
|
|
_screen.hLine(r2.left + 1, y + 2, r2.right - 2, color);
|
|
r2 = r;
|
|
}
|
|
|
|
r2.top = r2.bottom - UP_DOWN_BOX_HEIGHT;
|
|
if (scroll == kScrollbarStateSinglePage) {
|
|
color = _color;
|
|
} else if (scroll == kScrollbarStateDown && state == kStateHighlight) {
|
|
color = _textcolorhi;
|
|
} else {
|
|
color = _textcolor;
|
|
}
|
|
|
|
// draws the 'down' button
|
|
box(r2.left, r2.top, r2.width(), UP_DOWN_BOX_HEIGHT, _color, _shadowcolor);
|
|
p0 = Common::Point(r2.left + r2.width() / 2, r2.top + (UP_DOWN_BOX_HEIGHT + arrowSize + 1) / 2);
|
|
p1 = Common::Point(p0.x - arrowSize, p0.y - arrowSize);
|
|
p2 = Common::Point(p0.x + arrowSize, p0.y - arrowSize);
|
|
for (; p1.x <= p2.x; ++p1.x)
|
|
_screen.drawLine(p0.x, p0.y, p1.x, p1.y, color);
|
|
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawCaret(const Common::Rect &r, bool erase, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
|
|
OverlayColor color = 0;
|
|
if (erase) {
|
|
color = _bgcolor;
|
|
} else {
|
|
color = getColor(state);
|
|
}
|
|
|
|
_screen.vLine(r.left, r.top, r.bottom - 2, color);
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
void ThemeClassic::drawLineSeparator(const Common::Rect &r, kState state) {
|
|
if (!_initOk)
|
|
return;
|
|
_screen.hLine(r.left - 1, r.top + r.height() / 2, r.right, _shadowcolor);
|
|
_screen.hLine(r.left, r.top + 1 + r.height() / 2, r.right, _color);
|
|
addDirtyRect(r);
|
|
}
|
|
|
|
// intern drawing
|
|
|
|
void ThemeClassic::restoreBackground(Common::Rect r) {
|
|
r.clip(_screen.w, _screen.h);
|
|
r.clip(_drawArea);
|
|
#ifndef OLDGUI_TRANSPARENCY
|
|
_screen.fillRect(r, _bgcolor);
|
|
#else
|
|
if (_dialog) {
|
|
if (!_dialog->screen.pixels) {
|
|
_screen.fillRect(r, _bgcolor);
|
|
return;
|
|
}
|
|
const OverlayColor *src = (const OverlayColor*)_dialog->screen.getBasePtr(r.left, r.top);
|
|
OverlayColor *dst = (OverlayColor*)_screen.getBasePtr(r.left, r.top);
|
|
|
|
int h = r.height();
|
|
int w = r.width();
|
|
while (h--) {
|
|
memcpy(dst, src, w*sizeof(OverlayColor));
|
|
src += _dialog->screen.w;
|
|
dst += _screen.w;
|
|
}
|
|
} else {
|
|
_screen.fillRect(r, _bgcolor);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool ThemeClassic::addDirtyRect(Common::Rect r, bool save) {
|
|
// TODO: implement proper dirty rect handling
|
|
// FIXME: problem with the 'pitch'
|
|
r.clip(_screen.w, _screen.h);
|
|
r.clip(_drawArea);
|
|
_system->copyRectToOverlay((OverlayColor*)_screen.getBasePtr(r.left, r.top), _screen.w, r.left, r.top, r.width(), r.height());
|
|
if (_dialog && save) {
|
|
if (_dialog->screen.pixels) {
|
|
OverlayColor *dst = (OverlayColor*)_dialog->screen.getBasePtr(r.left, r.top);
|
|
const OverlayColor *src = (const OverlayColor*)_screen.getBasePtr(r.left, r.top);
|
|
int h = r.height();
|
|
while (h--) {
|
|
memcpy(dst, src, r.width()*sizeof(OverlayColor));
|
|
dst += _dialog->screen.w;
|
|
src += _screen.w;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ThemeClassic::box(int x, int y, int width, int height, OverlayColor colorA, OverlayColor colorB) {
|
|
if (y >= 0) {
|
|
_screen.hLine(x + 1, y, x + width - 2, colorA);
|
|
_screen.hLine(x, y + 1, x + width - 1, colorA);
|
|
}
|
|
int drawY = y;
|
|
if (drawY < 0) {
|
|
height += drawY;
|
|
drawY = 0;
|
|
}
|
|
_screen.vLine(x, drawY + 1, drawY + height - 2, colorA);
|
|
_screen.vLine(x + 1, drawY, drawY + height - 1, colorA);
|
|
|
|
if (y + height >= 0) {
|
|
_screen.hLine(x + 1, drawY + height - 2, x + width - 1, colorB);
|
|
_screen.hLine(x + 1, drawY + height - 1, x + width - 2, colorB);
|
|
_screen.vLine(x + width - 1, drawY + 1, drawY + height - 2, colorB);
|
|
_screen.vLine(x + width - 2, drawY + 1, drawY + height - 1, colorB);
|
|
}
|
|
}
|
|
|
|
void ThemeClassic::box(int x, int y, int w, int h) {
|
|
_screen.hLine(x, y, x + w - 1, _color);
|
|
_screen.hLine(x, y + h - 1, x +w - 1, _shadowcolor);
|
|
_screen.vLine(x, y, y + h - 1, _color);
|
|
_screen.vLine(x + w - 1, y, y + h - 1, _shadowcolor);
|
|
}
|
|
|
|
OverlayColor ThemeClassic::getColor(kState state) {
|
|
OverlayColor usedColor = _color;
|
|
switch (state) {
|
|
case kStateEnabled:
|
|
usedColor = _textcolor;
|
|
break;
|
|
|
|
case kStateHighlight:
|
|
usedColor = _textcolorhi;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return usedColor;
|
|
}
|
|
|
|
#ifdef OLDGUI_TRANSPARENCY
|
|
void ThemeClassic::blendScreenToDialog() {
|
|
Common::Rect rect(0, 0, _screen.w, _screen.h);
|
|
|
|
if (!rect.isValidRect())
|
|
return;
|
|
|
|
if (_system->hasFeature(OSystem::kFeatureOverlaySupportsAlpha)) {
|
|
int a, r, g, b;
|
|
uint8 aa, ar, ag, ab;
|
|
_system->colorToARGB(_bgcolor, aa, ar, ag, ab);
|
|
a = aa*3/(3+1);
|
|
if (a < 1)
|
|
return;
|
|
r = ar * a;
|
|
g = ag * a;
|
|
b = ab * a;
|
|
|
|
OverlayColor *ptr = (OverlayColor*)_dialog->screen.getBasePtr(rect.left, rect.top);
|
|
|
|
int h = rect.height();
|
|
int w = rect.width();
|
|
while (h--) {
|
|
for (int i = 0; i < w; i++) {
|
|
_system->colorToARGB(ptr[i], aa, ar, ag, ab);
|
|
int a2 = aa + a - (a*aa)/255;
|
|
ptr[i] = _system->ARGBToColor(a2,
|
|
((255-a)*aa*ar/255+r)/a2,
|
|
((255-a)*aa*ag/255+g)/a2,
|
|
((255-a)*aa*ab/255+b)/a2);
|
|
}
|
|
ptr += _screen.w;
|
|
}
|
|
} else {
|
|
int r, g, b;
|
|
uint8 ar, ag, ab;
|
|
_system->colorToRGB(_bgcolor, ar, ag, ab);
|
|
r = ar * 3;
|
|
g = ag * 3;
|
|
b = ab * 3;
|
|
|
|
OverlayColor *ptr = (OverlayColor*)_dialog->screen.getBasePtr(rect.left, rect.top);
|
|
|
|
int h = rect.height();
|
|
int w = rect.width();
|
|
|
|
while (h--) {
|
|
for (int i = 0; i < w; i++) {
|
|
_system->colorToRGB(ptr[i], ar, ag, ab);
|
|
ptr[i] = _system->RGBToColor((ar + r) / (3+1),
|
|
(ag + g) / (3+1),
|
|
(ab + b) / (3+1));
|
|
}
|
|
ptr += _screen.w;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} // end of namespace GUI
|
|
|