einstein/cpp-source/widgets.cpp

1014 lines
23 KiB
C++

#include "widgets.h"
#include "main.h"
#include "utils.h"
#include "sound.h"
//////////////////////////////////////////////////////////////////
//
// Button
//
//////////////////////////////////////////////////////////////////
Button::Button(int x, int y, const std::wstring &name, Command *cmd,
bool transparent)
{
image = loadImage(name, transparent);
highlighted = adjustBrightness(image, 1.5, transparent);
left = x;
top = y;
width = image->w;
height = image->h;
mouseInside = false;
command = cmd;
}
Button::Button(int x, int y, int w, int h, Font *font,
int fR, int fG, int fB, int hR, int hG, int hB,
const std::wstring &text, Command *cmd)
{
left = x;
top = y;
width = w;
height = h;
SDL_Surface *s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0/*0xFF000000*/);
SDL_Rect src = { x, y, width, height };
SDL_Rect dst = { 0, 0, width, height };
SDL_BlitSurface(screen.getSurface(), &src, s, &dst);
int tW, tH;
font->getSize(text, tW, tH);
font->draw(s, (width - tW) / 2, (height - tH) / 2, fR, fG, fB, true, text);
image = SDL_DisplayFormat(s);
SDL_BlitSurface(screen.getSurface(), &src, s, &dst);
font->draw(s, (width - tW) / 2, (height - tH) / 2, hR, hG, hB, true, text);
highlighted = SDL_DisplayFormat(s);
SDL_FreeSurface(s);
mouseInside = false;
command = cmd;
}
Button::Button(int x, int y, int w, int h, Font *font,
int r, int g, int b, const std::wstring &bg,
const std::wstring &text, bool bevel, Command *cmd)
{
left = x;
top = y;
width = w;
height = h;
SDL_Surface *s = screen.getSurface();
image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask,
s->format->Bmask, s->format->Amask);
SDL_Surface *tile = loadImage(bg, true);
SDL_Rect src = { 0, 0, tile->w, tile->h };
SDL_Rect dst = { 0, 0, tile->w, tile->h };
for (int j = 0; j < height; j += tile->h)
for (int i = 0; i < width; i += tile->w) {
dst.x = i;
dst.y = j;
SDL_BlitSurface(tile, &src, image, &dst);
}
SDL_FreeSurface(tile);
if (bevel) {
SDL_LockSurface(image);
drawBevel(image, 0, 0, width, height, false, 1);
drawBevel(image, 1, 1, width - 2, height - 2, true, 1);
SDL_UnlockSurface(image);
}
int tW, tH;
font->getSize(text, tW, tH);
font->draw(image, (width - tW) / 2, (height - tH) / 2, r, g, b, true, text);
highlighted = adjustBrightness(image, 1.5, false);
SDL_SetColorKey(image, SDL_SRCCOLORKEY, getCornerPixel(image));
SDL_SetColorKey(highlighted, SDL_SRCCOLORKEY, getCornerPixel(highlighted));
mouseInside = false;
command = cmd;
}
Button::Button(int x, int y, int w, int h, Font *font,
int r, int g, int b, const std::wstring &bg,
const std::wstring &text, Command *cmd)
{
left = x;
top = y;
width = w;
height = h;
SDL_Surface *s = screen.getSurface();
image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask,
s->format->Bmask, s->format->Amask);
SDL_Surface *tile = loadImage(bg);
SDL_Rect src = { 0, 0, tile->w, tile->h };
SDL_Rect dst = { 0, 0, tile->w, tile->h };
for (int j = 0; j < height; j += tile->h)
for (int i = 0; i < width; i += tile->w) {
dst.x = i;
dst.y = j;
SDL_BlitSurface(tile, &src, image, &dst);
}
SDL_FreeSurface(tile);
SDL_LockSurface(image);
drawBevel(image, 0, 0, width, height, false, 1);
drawBevel(image, 1, 1, width - 2, height - 2, true, 1);
SDL_UnlockSurface(image);
int tW, tH;
font->getSize(text, tW, tH);
font->draw(image, (width - tW) / 2, (height - tH) / 2, r, g, b, true, text);
highlighted = adjustBrightness(image, 1.5, false);
mouseInside = false;
command = cmd;
}
Button::~Button()
{
SDL_FreeSurface(image);
SDL_FreeSurface(highlighted);
}
void Button::draw()
{
if (mouseInside)
screen.draw(left, top, highlighted);
else
screen.draw(left, top, image);
screen.addRegionToUpdate(left, top, width, height);
}
void Button::getBounds(int &l, int &t, int &w, int &h)
{
l = left;
t = top;
w = width;
h = height;
}
bool Button::onMouseButtonDown(int button, int x, int y)
{
if (isInRect(x, y, left, top, width, height)) {
sound->play(L"click.wav");
if (command)
command->doAction();
return true;
} else
return false;
}
bool Button::onMouseMove(int x, int y)
{
bool in = isInRect(x, y, left, top, width, height);
if (in != mouseInside) {
mouseInside = in;
draw();
}
return false;
}
//////////////////////////////////////////////////////////////////
//
// KeyAccel
//
//////////////////////////////////////////////////////////////////
KeyAccel::KeyAccel(SDLKey sym, Command *cmd)
{
command = cmd;
key = sym;
}
bool KeyAccel::onKeyDown(SDLKey k, unsigned char ch)
{
if (key == k) {
if (command)
command->doAction();
return true;
} else
return false;
}
//////////////////////////////////////////////////////////////////
//
// Area
//
//////////////////////////////////////////////////////////////////
Area::Area()
{
timer = NULL;
}
Area::~Area()
{
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++) {
Widget *w = *i;
if (w && w->destroyByArea() && (! notManagedWidgets.count(w)))
delete w;
}
}
void Area::add(Widget *widget, bool managed)
{
widgets.push_back(widget);
if (! managed)
notManagedWidgets.insert(widget);
widget->setParent(this);
}
void Area::remove(Widget *widget)
{
widgets.remove(widget);
notManagedWidgets.insert(widget);
}
void Area::handleEvent(const SDL_Event &event)
{
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++)
if ((*i)->onMouseButtonDown(event.button.button,
event.button.x, event.button.y))
return;
break;
case SDL_MOUSEBUTTONUP:
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++)
if ((*i)->onMouseButtonUp(event.button.button,
event.button.x, event.button.y))
return;
break;
case SDL_MOUSEMOTION:
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++)
if ((*i)->onMouseMove(event.motion.x, event.motion.y))
return;
break;
case SDL_VIDEOEXPOSE:
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++)
(*i)->draw();
break;
case SDL_KEYDOWN:
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++)
if ((*i)->onKeyDown(event.key.keysym.sym,
(unsigned char)event.key.keysym.unicode))
return;
break;
case SDL_QUIT:
exit(0);
}
}
void Area::run()
{
terminate = false;
SDL_Event event;
Uint32 lastTimer = 0;
draw();
screen.showMouse();
bool runTimer = timer ? true : false;
bool dispetchEvent;
while (! terminate) {
dispetchEvent = true;
if (! timer) {
SDL_WaitEvent(&event);
} else {
Uint32 now = SDL_GetTicks();
if (now - lastTimer > time) {
lastTimer = now;
runTimer = true;
}
if (! SDL_PollEvent(&event)) {
if (! runTimer) {
SDL_Delay(20);
continue;
} else
dispetchEvent = false;
}
}
screen.hideMouse();
if (runTimer) {
if (timer)
timer->onTimer();
runTimer = false;
}
if (dispetchEvent)
handleEvent(event);
if (! terminate) {
screen.showMouse();
screen.flush();
}
}
}
void Area::finishEventLoop()
{
terminate = true;
}
void Area::draw()
{
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++)
(*i)->draw();
}
void Area::setTimer(Uint32 interval, TimerHandler *t)
{
time = interval;
timer = t;
}
void Area::updateMouse()
{
int x, y;
SDL_GetMouseState(&x, &y);
for (WidgetsList::iterator i = widgets.begin(); i != widgets.end(); i++)
if ((*i)->onMouseMove(x, y))
return;
}
//////////////////////////////////////////////////////////////////
//
// AnyKeyAccel
//
//////////////////////////////////////////////////////////////////
AnyKeyAccel::AnyKeyAccel()
{
command = NULL;
}
AnyKeyAccel::AnyKeyAccel(Command *cmd)
{
command = cmd;
}
AnyKeyAccel::~AnyKeyAccel()
{
}
bool AnyKeyAccel::onKeyDown(SDLKey key, unsigned char ch)
{
if (((key >= SDLK_NUMLOCK) && (key <= SDLK_COMPOSE)) ||
(key == SDLK_TAB) || (key == SDLK_UNKNOWN))
return false;
if (command)
command->doAction();
else
area->finishEventLoop();
return true;
}
bool AnyKeyAccel::onMouseButtonDown(int button, int x, int y)
{
if (command)
command->doAction();
else
area->finishEventLoop();
return true;
}
//////////////////////////////////////////////////////////////////
//
// Window
//
//////////////////////////////////////////////////////////////////
Window::Window(int x, int y, int w, int h, const std::wstring &bg,
bool frameWidth, bool raised)
{
left = x;
top = y;
width = w;
height = h;
SDL_Surface *s = screen.getSurface();
SDL_Surface *win = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask,
s->format->Bmask, s->format->Amask);
SDL_Surface *tile = loadImage(bg);
SDL_Rect src = { 0, 0, tile->w, tile->h };
SDL_Rect dst = { 0, 0, tile->w, tile->h };
for (int j = 0; j < height; j += tile->h)
for (int i = 0; i < width; i += tile->w) {
dst.x = i;
dst.y = j;
SDL_BlitSurface(tile, &src, win, &dst);
}
SDL_FreeSurface(tile);
SDL_LockSurface(win);
double k = 2.6;
double f = 0.1;
for (int i = 0; i < frameWidth; i++) {
double ltK, rbK;
if (raised) {
ltK = k; rbK = f;
} else {
ltK = f; rbK = k;
}
for (int j = i; j < height - i - 1; j++)
adjustBrightness(win, i, j, ltK);
for (int j = i; j < width - i; j++)
adjustBrightness(win, j, i, ltK);
for (int j = i+1; j < height - i; j++)
adjustBrightness(win, width - i - 1, j, rbK);
for (int j = i; j < width - i - 1; j++)
adjustBrightness(win, j, height - i - 1, rbK);
k -= 0.2;
f += 0.1;
}
SDL_UnlockSurface(win);
background = SDL_DisplayFormat(win);
SDL_FreeSurface(win);
}
Window::~Window()
{
SDL_FreeSurface(background);
}
void Window::draw()
{
screen.draw(left, top, background);
screen.addRegionToUpdate(left, top, width, height);
}
//////////////////////////////////////////////////////////////////
//
// Label
//
//////////////////////////////////////////////////////////////////
Label::Label(Font *f, int x, int y, int r, int g, int b, std::wstring s,
bool sh): text(s)
{
font = f;
left = x;
top = y;
red = r;
green = g;
blue = b;
hAlign = ALIGN_LEFT;
vAlign = ALIGN_TOP;
shadow = sh;
}
Label::Label(Font *f, int x, int y, int w, int h, HorAlign hA, VerAlign vA,
int r, int g, int b, const std::wstring &s):
text(s)
{
font = f;
left = x;
top = y;
red = r;
green = g;
blue = b;
hAlign = hA;
vAlign = vA;
width = w;
height = h;
shadow = true;
}
void Label::draw()
{
int w, h, x, y;
font->getSize(text, w, h);
switch (hAlign) {
case ALIGN_RIGHT: x = left + width - w; break;
case ALIGN_CENTER: x = left + (width - w) / 2; break;
default: x = left;
}
switch (vAlign) {
case ALIGN_BOTTOM: y = top + height - h; break;
case ALIGN_MIDDLE: y = top + (height - h) / 2; break;
default: y = top;
}
font->draw(x, y, red,green,blue, shadow, text);
screen.addRegionToUpdate(x, y, w, h);
}
//////////////////////////////////////////////////////////////////
//
// InputField
//
//////////////////////////////////////////////////////////////////
InputField::InputField(int x, int y, int w, int h, const std::wstring &background,
std::wstring &s, int maxLen, int r, int g, int b, Font *f):
Window(x, y, w, h, background, 1, false), text(s)
{
maxLength = maxLen;
red = r;
green = g;
blue = b;
font = f;
moveCursor(text.length());
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
SDL_EnableUNICODE(1);
}
InputField::~InputField()
{
SDL_EnableKeyRepeat(0, 0);
}
void InputField::draw()
{
Window::draw();
SDL_Rect rect = { left+1, top+1, width-2, height-2 };
SDL_SetClipRect(screen.getSurface(), &rect);
font->draw(left+1, top+1, red,green,blue, true, text);
if (cursorVisible) {
int pos = 0;
if (cursorPos > 0)
pos += font->getWidth(text.substr(0, cursorPos));
for (int i = 2; i < height-2; i++) {
screen.setPixel(left + pos, top + i, red, green, blue);
screen.setPixel(left + pos + 1, top + i, red, green, blue);
}
}
SDL_SetClipRect(screen.getSurface(), NULL);
}
void InputField::setParent(Area *a)
{
Window::setParent(a);
area->setTimer(100, this);
}
void InputField::onTimer()
{
Uint32 now = SDL_GetTicks();
if (now - lastCursor > 1000) {
cursorVisible = ! cursorVisible;
lastCursor = now;
draw();
}
}
bool InputField::onKeyDown(SDLKey key, unsigned char translatedChar)
{
switch (key) {
case SDLK_BACKSPACE:
if (cursorPos > 0) {
text.erase(cursorPos - 1, 1);
moveCursor(cursorPos - 1);
} else
moveCursor(cursorPos);
draw();
return true;
case SDLK_LEFT:
if (cursorPos > 0)
moveCursor(cursorPos - 1);
else
moveCursor(cursorPos);
draw();
return true;
case SDLK_RIGHT:
if (cursorPos < (int)text.length())
moveCursor(cursorPos + 1);
else
moveCursor(cursorPos);
draw();
return true;
case SDLK_HOME:
moveCursor(0);
draw();
return true;
case SDLK_END:
moveCursor(text.length());
draw();
return true;
case SDLK_DELETE:
if (cursorPos < (int)text.length())
text.erase(cursorPos, 1);
moveCursor(cursorPos);
draw();
return true;
default: ;
}
if (translatedChar > 31)
onCharTyped(translatedChar);
return false;
}
bool InputField::onKeyUp(SDLKey key)
{
return false;
}
void InputField::onCharTyped(unsigned char ch)
{
if ((int)text.length() < maxLength) {
wchar_t buf[2];
buf[0] = ch;
buf[1] = 0;
text.insert(cursorPos, buf);
moveCursor(cursorPos + 1);
} else
moveCursor(cursorPos);
draw();
}
void InputField::moveCursor(int pos)
{
lastCursor = SDL_GetTicks();
cursorVisible = true;
cursorPos = pos;
}
//////////////////////////////////////////////////////////////////
//
// Checkbox
//
//////////////////////////////////////////////////////////////////
Checkbox::Checkbox(int x, int y, int w, int h, Font *font,
int r, int g, int b, const std::wstring &bg,
bool &chk): checked(chk)
{
left = x;
top = y;
width = w;
height = h;
checked = chk;
SDL_Surface *s = screen.getSurface();
image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height,
s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask,
s->format->Bmask, s->format->Amask);
SDL_Surface *tile = loadImage(bg);
SDL_Rect src = { 0, 0, tile->w, tile->h };
SDL_Rect dst = { 0, 0, tile->w, tile->h };
for (int j = 0; j < height; j += tile->h)
for (int i = 0; i < width; i += tile->w) {
dst.x = i;
dst.y = j;
SDL_BlitSurface(tile, &src, image, &dst);
}
SDL_FreeSurface(tile);
SDL_LockSurface(image);
drawBevel(image, 0, 0, width, height, false, 1);
drawBevel(image, 1, 1, width - 2, height - 2, true, 1);
SDL_UnlockSurface(image);
highlighted = adjustBrightness(image, 1.5, false);
checkedImage = SDL_DisplayFormat(image);
int tW, tH;
font->getSize(L"X", tW, tH);
tH += 2;
tW += 2;
font->draw(checkedImage, (width - tW) / 2, (height - tH) / 2, r, g, b,
true, L"X");
checkedHighlighted = adjustBrightness(checkedImage, 1.5, false);
mouseInside = false;
}
Checkbox::~Checkbox()
{
SDL_FreeSurface(image);
SDL_FreeSurface(highlighted);
SDL_FreeSurface(checkedImage);
SDL_FreeSurface(checkedHighlighted);
}
void Checkbox::draw()
{
if (checked) {
if (mouseInside)
screen.draw(left, top, checkedHighlighted);
else
screen.draw(left, top, checkedImage);
} else {
if (mouseInside)
screen.draw(left, top, highlighted);
else
screen.draw(left, top, image);
}
screen.addRegionToUpdate(left, top, width, height);
}
void Checkbox::getBounds(int &l, int &t, int &w, int &h)
{
l = left;
t = top;
w = width;
h = height;
}
bool Checkbox::onMouseButtonDown(int button, int x, int y)
{
if (isInRect(x, y, left, top, width, height)) {
sound->play(L"click.wav");
checked = ! checked;
draw();
return true;
} else
return false;
}
bool Checkbox::onMouseMove(int x, int y)
{
bool in = isInRect(x, y, left, top, width, height);
if (in != mouseInside) {
mouseInside = in;
draw();
}
return false;
}
//////////////////////////////////////////////////////////////////////////////
//
// Picture
//
//////////////////////////////////////////////////////////////////////////////
Picture::Picture(int x, int y, const std::wstring &name, bool transparent)
{
image = loadImage(name, transparent);
left = x;
top = y;
width = image->w;
height = image->h;
managed = true;
}
Picture::Picture(int x, int y, SDL_Surface *img)
{
image = img;
left = x;
top = y;
width = image->w;
height = image->h;
managed = false;
}
Picture::~Picture()
{
if (managed)
SDL_FreeSurface(image);
}
void Picture::draw()
{
screen.draw(left, top, image);
screen.addRegionToUpdate(left, top, width, height);
}
void Picture::moveX(const int newX)
{
left = newX;
}
void Picture::getBounds(int &l, int &t, int &w, int &h)
{
l = left;
t = top;
w = width;
h = height;
}
//////////////////////////////////////////////////////////////////
//
// Slider
//
//////////////////////////////////////////////////////////////////
Slider::Slider(int x, int y, int w, int h, float &v): value(v)
{
left = x;
top = y;
width = w;
height = h;
background = NULL;
createSlider(height);
highlight = false;
dragging = false;
}
Slider::~Slider()
{
if (background)
SDL_FreeSurface(background);
if (slider)
SDL_FreeSurface(slider);
if (activeSlider)
SDL_FreeSurface(activeSlider);
}
void Slider::draw()
{
if (! background)
createBackground();
screen.draw(left, top, background);
screen.addRegionToUpdate(left, top, width, height);
int posX = valueToX(value);
SDL_Surface *s = highlight ? activeSlider : slider;
screen.draw(left + posX, top, s);
}
void Slider::createBackground()
{
background = screen.createSubimage(left, top, width, height);
int y = height / 2;
SDL_LockSurface(background);
drawBevel(background, 0, y - 2, width, 4, false, 1);
SDL_UnlockSurface(background);
}
void Slider::createSlider(int size)
{
SDL_Surface *s = screen.getSurface();
SDL_Surface *image = SDL_CreateRGBSurface(SDL_SWSURFACE, size, size,
s->format->BitsPerPixel, s->format->Rmask, s->format->Gmask,
s->format->Bmask, s->format->Amask);
SDL_Surface *tile = loadImage(L"blue.bmp");
SDL_Rect src = { 0, 0, tile->w, tile->h };
SDL_Rect dst = { 0, 0, tile->w, tile->h };
for (int j = 0; j < size; j += tile->h)
for (int i = 0; i < size; i += tile->w) {
dst.x = i;
dst.y = j;
SDL_BlitSurface(tile, &src, image, &dst);
}
SDL_FreeSurface(tile);
SDL_LockSurface(image);
drawBevel(image, 0, 0, size, size, false, 1);
drawBevel(image, 1, 1, size - 2, size - 2, true, 1);
SDL_UnlockSurface(image);
activeSlider = adjustBrightness(image, 1.5, false);
slider = SDL_DisplayFormat(image);
SDL_FreeSurface(image);
}
bool Slider::onMouseButtonDown(int button, int x, int y)
{
bool in = isInRect(x, y, left, top, width, height);
if (in) {
int sliderX = valueToX(value);
bool hl = isInRect(x, y, left + sliderX, top, height, height);
if (hl) {
dragging = true;
dragOffsetX = x - left - sliderX;
}
}
return in;
}
bool Slider::onMouseButtonUp(int button, int x, int y)
{
if (dragging) {
dragging = false;
return true;
} else
return false;
}
int Slider::valueToX(float value)
{
if (value < 0)
value = 0.0f;
if (value > 1)
value = 1.0f;
return (int)(((float)(width - height)) * value);
}
float Slider::xToValue(int pos)
{
if (0 > pos)
pos = 0;
if (width - height < pos)
pos = width - height;
return (float)pos / (float)(width - height);
}
bool Slider::onMouseMove(int x, int y)
{
if (dragging) {
float val = xToValue(x - left - dragOffsetX);
if (val != value) {
value = val;
draw();
}
return true;
}
bool in = isInRect(x, y, left, top, width, height);
if (in) {
int sliderX = valueToX(value);
bool hl = isInRect(x, y, left + sliderX, top, height, height);
if (hl != highlight) {
highlight = hl;
draw();
}
} else
if (highlight) {
highlight = false;
draw();
}
return in;
}