Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,483 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ags/shared/ac/sprite_cache.h"
#include "ags/shared/ac/game_struct_defines.h"
#include "ags/shared/font/fonts.h"
#include "ags/shared/gui/gui_button.h"
#include "ags/shared/gui/gui_main.h" // TODO: extract helper functions
#include "ags/shared/util/stream.h"
#include "ags/shared/util/string_utils.h"
#include "ags/globals.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
FrameAlignment ConvertLegacyButtonAlignment(LegacyButtonAlignment align) {
switch (align) {
case kLegacyButtonAlign_TopCenter:
return kAlignTopCenter;
case kLegacyButtonAlign_TopLeft:
return kAlignTopLeft;
case kLegacyButtonAlign_TopRight:
return kAlignTopRight;
case kLegacyButtonAlign_CenterLeft:
return kAlignMiddleLeft;
case kLegacyButtonAlign_Centered:
return kAlignMiddleCenter;
case kLegacyButtonAlign_CenterRight:
return kAlignMiddleRight;
case kLegacyButtonAlign_BottomLeft:
return kAlignBottomLeft;
case kLegacyButtonAlign_BottomCenter:
return kAlignBottomCenter;
case kLegacyButtonAlign_BottomRight:
return kAlignBottomRight;
default:
break;
}
return kAlignNone;
}
GUIButton::GUIButton() {
_image = -1;
_mouseOverImage = -1;
_pushedImage = -1;
_currentImage = -1;
Font = 0;
TextColor = 0;
TextAlignment = kAlignTopCenter;
ClickAction[kGUIClickLeft] = kGUIAction_RunScript;
ClickAction[kGUIClickRight] = kGUIAction_RunScript;
ClickData[kGUIClickLeft] = 0;
ClickData[kGUIClickRight] = 0;
IsPushed = false;
IsMouseOver = false;
_placeholder = kButtonPlace_None;
_unnamed = true;
_scEventCount = 1;
_scEventNames[0] = "Click";
_scEventArgs[0] = "GUIControl *control, MouseButton button";
}
bool GUIButton::HasAlphaChannel() const {
return ((_currentImage > 0) && is_sprite_alpha(_currentImage)) ||
(!_unnamed && is_font_antialiased(Font));
}
int32_t GUIButton::GetCurrentImage() const {
return _currentImage;
}
int32_t GUIButton::GetNormalImage() const {
return _image;
}
int32_t GUIButton::GetMouseOverImage() const {
return _mouseOverImage;
}
int32_t GUIButton::GetPushedImage() const {
return _pushedImage;
}
GUIButtonPlaceholder GUIButton::GetPlaceholder() const {
return _placeholder;
}
const String &GUIButton::GetText() const {
return _text;
}
bool GUIButton::IsImageButton() const {
return _image > 0;
}
bool GUIButton::IsClippingImage() const {
return (Flags & kGUICtrl_Clip) != 0;
}
Rect GUIButton::CalcGraphicRect(bool clipped) {
if (clipped)
return RectWH(0, 0, _width, _height);
// TODO: need to find a way to cache image and text position, or there'll be some repetition
Rect rc = RectWH(0, 0, _width, _height);
if (IsImageButton()) {
if (IsClippingImage())
return rc;
// Main button graphic
if (_currentImage >= 0 && _GP(spriteset).DoesSpriteExist(_currentImage))
rc = SumRects(rc, RectWH(0, 0, get_adjusted_spritewidth(_currentImage), get_adjusted_spriteheight(_currentImage)));
// Optionally merge with the inventory pic
if (_placeholder != kButtonPlace_None && _G(gui_inv_pic) >= 0) {
Size inv_sz = Size(get_adjusted_spritewidth(_G(gui_inv_pic)),
get_adjusted_spriteheight(_G(gui_inv_pic)));
GUIButtonPlaceholder place = _placeholder;
if (place == kButtonPlace_InvItemAuto) {
place = ((inv_sz.Width > _width - 6) || (inv_sz.Height > _height - 6)) ?
kButtonPlace_InvItemStretch : kButtonPlace_InvItemCenter;
}
Rect inv_rc = (place == kButtonPlace_InvItemStretch) ?
RectWH(0 + 3, 0 + 3, _width - 6, _height - 6) :
RectWH(0 + _width / 2 - inv_sz.Width / 2,
0 + _height / 2 - inv_sz.Height / 2,
inv_sz.Width, inv_sz.Height);
rc = SumRects(rc, inv_rc);
}
}
// Optionally merge with the button text
if (!IsImageButton() || ((_placeholder == kButtonPlace_None) && !_unnamed)) {
PrepareTextToDraw();
Rect frame = RectWH(0 + 2, 0 + 2, _width - 4, _height - 4);
if (IsPushed && IsMouseOver) {
frame.Left++;
frame.Top++;
}
rc = SumRects(rc, GUI::CalcTextGraphicalRect(_textToDraw.GetCStr(), Font, frame, TextAlignment));
}
return rc;
}
void GUIButton::Draw(Bitmap *ds, int x, int y) {
bool draw_disabled = !IsGUIEnabled(this);
// if it's "Unchanged when disabled" or "GUI Off", don't grey out
if ((GUI::Options.DisabledStyle == kGuiDis_Unchanged) ||
(GUI::Options.DisabledStyle == kGuiDis_Off)) {
draw_disabled = false;
}
// TODO: should only change properties in reaction to particular events
if (_currentImage <= 0 || draw_disabled)
_currentImage = _image;
if (draw_disabled && (GUI::Options.DisabledStyle == kGuiDis_Blackout))
// buttons off when disabled - no point carrying on
return;
if (IsImageButton())
DrawImageButton(ds, x, y, draw_disabled);
// CHECKME: why don't draw frame if no Text? this will make button completely invisible!
else if (!_text.IsEmpty())
DrawTextButton(ds, x, y, draw_disabled);
}
void GUIButton::SetClipImage(bool on) {
if (on != ((Flags & kGUICtrl_Clip) != 0))
MarkChanged();
if (on)
Flags |= kGUICtrl_Clip;
else
Flags &= ~kGUICtrl_Clip;
}
void GUIButton::SetCurrentImage(int32_t image) {
if (_currentImage == image)
return;
_currentImage = image;
MarkChanged();
}
void GUIButton::SetMouseOverImage(int32_t image) {
if (_mouseOverImage == image)
return;
_mouseOverImage = image;
UpdateCurrentImage();
}
void GUIButton::SetNormalImage(int32_t image) {
if (_image == image)
return;
_image = image;
UpdateCurrentImage();
}
void GUIButton::SetPushedImage(int32_t image) {
if (_pushedImage == image)
return;
_pushedImage = image;
UpdateCurrentImage();
}
void GUIButton::SetImages(int32_t normal, int32_t over, int32_t pushed) {
_image = normal;
_mouseOverImage = over;
_pushedImage = pushed;
UpdateCurrentImage();
}
void GUIButton::SetText(const String &text) {
if (_text == text)
return;
_text = text;
// Active inventory item placeholders
if (_text.CompareNoCase("(INV)") == 0)
// Stretch to fit button
_placeholder = kButtonPlace_InvItemStretch;
else if (_text.CompareNoCase("(INVNS)") == 0)
// Draw at actual size
_placeholder = kButtonPlace_InvItemCenter;
else if (_text.CompareNoCase("(INVSHR)") == 0)
// Stretch if too big, actual size if not
_placeholder = kButtonPlace_InvItemAuto;
else
_placeholder = kButtonPlace_None;
// TODO: find a way to remove this bogus limitation ("New Button" is a valid Text too)
_unnamed = _text.IsEmpty() || _text.Compare("New Button") == 0;
MarkChanged();
}
bool GUIButton::OnMouseDown() {
if (!IsImageButton())
MarkChanged();
IsPushed = true;
UpdateCurrentImage();
return false;
}
void GUIButton::OnMouseEnter() {
if (IsPushed && !IsImageButton())
MarkChanged();
IsMouseOver = true;
UpdateCurrentImage();
}
void GUIButton::OnMouseLeave() {
if (IsPushed && !IsImageButton())
MarkChanged();
IsMouseOver = false;
UpdateCurrentImage();
}
void GUIButton::OnMouseUp() {
if (IsMouseOver) {
if (IsGUIEnabled(this) && IsClickable())
IsActivated = true;
}
if (IsPushed && !IsImageButton())
MarkChanged();
IsPushed = false;
UpdateCurrentImage();
}
void GUIButton::UpdateCurrentImage() {
int new_image = _currentImage;
if (IsPushed && (_pushedImage > 0)) {
new_image = _pushedImage;
} else if (IsMouseOver && (_mouseOverImage > 0)) {
new_image = _mouseOverImage;
} else {
new_image = _image;
}
SetCurrentImage(new_image);
}
void GUIButton::WriteToFile(Stream *out) const {
GUIObject::WriteToFile(out);
out->WriteInt32(_image);
out->WriteInt32(_mouseOverImage);
out->WriteInt32(_pushedImage);
out->WriteInt32(Font);
out->WriteInt32(TextColor);
out->WriteInt32(ClickAction[kGUIClickLeft]);
out->WriteInt32(ClickAction[kGUIClickRight]);
out->WriteInt32(ClickData[kGUIClickLeft]);
out->WriteInt32(ClickData[kGUIClickRight]);
StrUtil::WriteString(_text, out);
out->WriteInt32(TextAlignment);
}
void GUIButton::ReadFromFile(Stream *in, GuiVersion gui_version) {
GUIObject::ReadFromFile(in, gui_version);
_image = in->ReadInt32();
_mouseOverImage = in->ReadInt32();
_pushedImage = in->ReadInt32();
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
_currentImage = in->ReadInt32();
IsPushed = in->ReadInt32() != 0;
IsMouseOver = in->ReadInt32() != 0;
}
Font = in->ReadInt32();
TextColor = in->ReadInt32();
ClickAction[kGUIClickLeft] = (GUIClickAction)in->ReadInt32();
ClickAction[kGUIClickRight] = (GUIClickAction)in->ReadInt32();
ClickData[kGUIClickLeft] = in->ReadInt32();
ClickData[kGUIClickRight] = in->ReadInt32();
if (gui_version < kGuiVersion_350)
SetText(String::FromStreamCount(in, GUIBUTTON_LEGACY_TEXTLENGTH));
else
SetText(StrUtil::ReadString(in));
if (gui_version >= kGuiVersion_272a) {
if (gui_version < kGuiVersion_350) {
TextAlignment = ConvertLegacyButtonAlignment((LegacyButtonAlignment)in->ReadInt32());
in->ReadInt32(); // reserved1
} else {
TextAlignment = (FrameAlignment)in->ReadInt32();
}
} else {
TextAlignment = kAlignTopCenter;
}
if (TextColor == 0)
TextColor = 16;
_currentImage = _image;
}
void GUIButton::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
GUIObject::ReadFromSavegame(in, svg_ver);
// Properties
_image = in->ReadInt32();
_mouseOverImage = in->ReadInt32();
_pushedImage = in->ReadInt32();
Font = in->ReadInt32();
TextColor = in->ReadInt32();
SetText(StrUtil::ReadString(in));
if (svg_ver >= kGuiSvgVersion_350)
TextAlignment = (FrameAlignment)in->ReadInt32();
// Dynamic state
_currentImage = in->ReadInt32();
// Update current state after reading
IsPushed = false;
IsMouseOver = false;
}
void GUIButton::WriteToSavegame(Stream *out) const {
// Properties
GUIObject::WriteToSavegame(out);
out->WriteInt32(_image);
out->WriteInt32(_mouseOverImage);
out->WriteInt32(_pushedImage);
out->WriteInt32(Font);
out->WriteInt32(TextColor);
StrUtil::WriteString(GetText(), out);
out->WriteInt32(TextAlignment);
// Dynamic state
out->WriteInt32(_currentImage);
}
void GUIButton::DrawImageButton(Bitmap *ds, int x, int y, bool draw_disabled) {
assert(_currentImage >= 0);
// NOTE: the CLIP flag only clips the image, not the text
if (IsClippingImage() && !GUI::Options.ClipControls)
ds->SetClip(RectWH(x, y, _width, _height));
if (_GP(spriteset).DoesSpriteExist(_currentImage))
draw_gui_sprite(ds, _currentImage, x, y, true);
// Draw active inventory item
if (_placeholder != kButtonPlace_None && _G(gui_inv_pic) >= 0) {
Size inv_sz = Size(get_adjusted_spritewidth(_G(gui_inv_pic)), get_adjusted_spriteheight(_G(gui_inv_pic)));
GUIButtonPlaceholder place = _placeholder;
if (place == kButtonPlace_InvItemAuto) {
place = ((inv_sz.Width > _width - 6) || (inv_sz.Height > _height - 6)) ?
kButtonPlace_InvItemStretch : kButtonPlace_InvItemCenter;
}
if (place == kButtonPlace_InvItemStretch) {
ds->StretchBlt(_GP(spriteset)[_G(gui_inv_pic)], RectWH(x + 3, y + 3, _width - 6, _height - 6),
kBitmap_Transparency);
} else {
draw_gui_sprite(ds, _G(gui_inv_pic),
x + _width / 2 - inv_sz.Width / 2,
y + _height / 2 - inv_sz.Height / 2,
true);
}
}
if ((draw_disabled) && (GUI::Options.DisabledStyle == kGuiDis_Greyout)) {
// darken the button when disabled
const Size sz = _GP(spriteset).GetSpriteResolution(_currentImage);
GUI::DrawDisabledEffect(ds, RectWH(x, y, sz.Width, sz.Height));
}
// Don't print Text of (INV) (INVSHR) (INVNS)
if ((_placeholder == kButtonPlace_None) && !_unnamed)
DrawText(ds, x, y, draw_disabled);
if (IsClippingImage() && !GUI::Options.ClipControls)
ds->ResetClip();
}
void GUIButton::DrawText(Bitmap *ds, int x, int y, bool draw_disabled) {
// TODO: need to find a way to cache Text prior to drawing;
// but that will require to update all gui controls when translation is changed in game
PrepareTextToDraw();
Rect frame = RectWH(x + 2, y + 2, _width - 4, _height - 4);
if (IsPushed && IsMouseOver) {
// move the Text a bit while pushed
frame.Left++;
frame.Top++;
}
color_t text_color = ds->GetCompatibleColor(TextColor);
if (draw_disabled)
text_color = ds->GetCompatibleColor(8);
GUI::DrawTextAligned(ds, _textToDraw.GetCStr(), Font, text_color, frame, TextAlignment);
}
void GUIButton::DrawTextButton(Bitmap *ds, int x, int y, bool draw_disabled) {
color_t draw_color = ds->GetCompatibleColor(7);
ds->FillRect(Rect(x, y, x + _width - 1, y + _height - 1), draw_color);
if (Flags & kGUICtrl_Default) {
draw_color = ds->GetCompatibleColor(16);
ds->DrawRect(Rect(x - 1, y - 1, x + _width, y + _height), draw_color);
}
// TODO: use color constants instead of literal numbers
if (!draw_disabled && IsMouseOver && IsPushed)
draw_color = ds->GetCompatibleColor(15);
else
draw_color = ds->GetCompatibleColor(8);
ds->DrawLine(Line(x, y + _height - 1, x + _width - 1, y + _height - 1), draw_color);
ds->DrawLine(Line(x + _width - 1, y, x + _width - 1, y + _height - 1), draw_color);
if (draw_disabled || (IsMouseOver && IsPushed))
draw_color = ds->GetCompatibleColor(8);
else
draw_color = ds->GetCompatibleColor(15);
ds->DrawLine(Line(x, y, x + _width - 1, y), draw_color);
ds->DrawLine(Line(x, y, x, y + _height - 1), draw_color);
DrawText(ds, x, y, draw_disabled);
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,150 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_BUTTON_H
#define AGS_SHARED_GUI_GUI_BUTTON_H
#include "common/std/vector.h"
#include "ags/engine/ac/button.h"
#include "ags/shared/gui/gui_object.h"
#include "ags/shared/util/string.h"
namespace AGS3 {
#define GUIBUTTON_LEGACY_TEXTLENGTH 50
namespace AGS {
namespace Shared {
enum GUIClickMouseButton {
kGUIClickLeft = 0,
kGUIClickRight = 1,
kNumGUIClicks
};
enum GUIClickAction {
kGUIAction_None = 0,
kGUIAction_SetMode = 1,
kGUIAction_RunScript = 2,
};
enum LegacyButtonAlignment {
kLegacyButtonAlign_TopCenter = 0,
kLegacyButtonAlign_TopLeft = 1,
kLegacyButtonAlign_TopRight = 2,
kLegacyButtonAlign_CenterLeft = 3,
kLegacyButtonAlign_Centered = 4,
kLegacyButtonAlign_CenterRight = 5,
kLegacyButtonAlign_BottomLeft = 6,
kLegacyButtonAlign_BottomCenter = 7,
kLegacyButtonAlign_BottomRight = 8,
};
// Defines button placeholder mode; the mode is set
// depending on special tags found in button text
enum GUIButtonPlaceholder {
kButtonPlace_None,
kButtonPlace_InvItemStretch,
kButtonPlace_InvItemCenter,
kButtonPlace_InvItemAuto
};
class GUIButton : public GUIObject {
public:
GUIButton();
bool HasAlphaChannel() const override;
int32_t GetCurrentImage() const;
int32_t GetNormalImage() const;
int32_t GetMouseOverImage() const;
int32_t GetPushedImage() const;
GUIButtonPlaceholder GetPlaceholder() const;
const String &GetText() const;
bool IsImageButton() const;
bool IsClippingImage() const;
// Operations
Rect CalcGraphicRect(bool clipped) override;
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
void SetClipImage(bool on);
void SetCurrentImage(int32_t image);
void SetMouseOverImage(int32_t image);
void SetNormalImage(int32_t image);
void SetPushedImage(int32_t image);
void SetImages(int32_t normal, int32_t over, int32_t pushed);
void SetText(const String &text);
// Events
bool OnMouseDown() override;
void OnMouseEnter() override;
void OnMouseLeave() override;
void OnMouseUp() override;
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
void WriteToFile(Stream *out) const override;
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
void WriteToSavegame(Shared::Stream *out) const override;
// TODO: these members are currently public; hide them later
public:
int32_t Font;
color_t TextColor;
FrameAlignment TextAlignment;
// Click actions for left and right mouse buttons
// NOTE: only left click is currently in use
GUIClickAction ClickAction[kNumGUIClicks];
int32_t ClickData[kNumGUIClicks];
bool IsPushed;
bool IsMouseOver;
private:
void DrawImageButton(Bitmap *ds, int x, int y, bool draw_disabled);
void DrawText(Bitmap *ds, int x, int y, bool draw_disabled);
void DrawTextButton(Bitmap *ds, int x, int y, bool draw_disabled);
void PrepareTextToDraw();
// Update current image depending on the button's state
void UpdateCurrentImage();
int32_t _image;
int32_t _mouseOverImage;
int32_t _pushedImage;
// Active displayed image
int32_t _currentImage;
// Text property set by user
String _text;
// type of content placeholder, if any
GUIButtonPlaceholder _placeholder;
// A flag indicating unnamed button; this is a convenience trick:
// buttons are created named "New Button" in the editor, and users
// often do not clear text when they want a graphic button.
bool _unnamed;
// Prepared text buffer/cache
String _textToDraw;
};
} // namespace Shared
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,216 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_DEFINES_H
#define AGS_SHARED_GUI_GUI_DEFINES_H
namespace AGS3 {
#define GUIMAGIC 0xcafebeef
#define MAX_GUIOBJ_EVENTS 10
#define TEXTWINDOW_PADDING_DEFAULT 3
// TODO: find out more about gui version history
//=============================================================================
// GUI Version history
//-----------------------------------------------------------------------------
//
// 2.1.4..... (100): Introduced Slider gui control. Gui count is now serialized
// in game file.
// 2.2.2..... (101): Introduced TextBox gui control.
// 2.3.0..... (102): Introduced ListBox gui control.
// 2.6.0..... (105): GUI custom Z-order support.
// 2.7.0..... (110): Added GUI OnClick handler.
// 2.7.2.???? (111): Added text alignment property to buttons.
// 2.7.2.???? (112): Added text alignment property to list boxes.
// 2.7.2.???? (113): Increased permitted length of GUI label text from 200 to
// 2048 characters.
// 2.7.2.???? (114): Obsoleted savegameindex[] array, and added
// ListBox.SaveGameSlots[] array instead.
// 2.7.2.???? (115): Added GUI Control z-order support.
//
// 3.3.0.1132 (116): Added kGUICtrl_Translated flag.
// 3.3.1.???? (117): Added padding variable for text window GUIs.
// 3.4.0 (118): Removed GUI limits
// 3.5.0 (119): Game data contains GUI properties that previously
// could be set only at runtime.
// Since then format value is defined as AGS version represented as NN,NN,NN,NN
//=============================================================================
enum GuiVersion {
// TODO: find out all corresponding engine version numbers
kGuiVersion_Initial = 0,
kGuiVersion_214 = 100,
kGuiVersion_222 = 101,
kGuiVersion_230 = 102,
kGuiVersion_unkn_103 = 103,
kGuiVersion_unkn_104 = 104,
kGuiVersion_260 = 105,
kGuiVersion_unkn_106 = 106,
kGuiVersion_unkn_107 = 107,
kGuiVersion_unkn_108 = 108,
kGuiVersion_unkn_109 = 109,
kGuiVersion_270 = 110,
kGuiVersion_272a = 111,
kGuiVersion_272b = 112,
kGuiVersion_272c = 113,
kGuiVersion_272d = 114,
kGuiVersion_272e = 115,
kGuiVersion_330 = 116,
kGuiVersion_331 = 117,
kGuiVersion_340 = 118,
kGuiVersion_350 = 119,
kGuiVersion_Current = kGuiVersion_350,
};
namespace AGS {
namespace Shared {
// GUIMain's style and behavior flags
enum GUIMainFlags {
kGUIMain_Clickable = 0x0001,
kGUIMain_TextWindow = 0x0002,
kGUIMain_Visible = 0x0004,
kGUIMain_Concealed = 0x0008,
// NOTE: currently default state is Visible to keep this backwards compatible;
// check later if this is still a wanted behavior
kGUIMain_DefFlags = kGUIMain_Clickable | kGUIMain_Visible,
// flags that had inverse meaning in old formats
kGUIMain_OldFmtXorMask = kGUIMain_Clickable
};
// GUIMain's legacy flags, now converted to GUIMainFlags on load
enum GUIMainLegacyFlags {
kGUIMain_LegacyTextWindow = 5
};
// GUIMain's style of getting displayed on screen
enum GUIPopupStyle {
// Normal GUI
kGUIPopupNormal = 0,
// Shown when the mouse cursor moves to the top of the screen
kGUIPopupMouseY = 1,
// Same as Normal, but pauses the game when shown
kGUIPopupModal = 2,
// Same as Normal, but is not removed when interface is off
kGUIPopupNoAutoRemove = 3,
// (legacy option) Normal GUI, initially off
// converts to kGUIPopupNormal with Visible = false
kGUIPopupLegacyNormalOff = 4
};
// The type of GUIControl
enum GUIControlType {
kGUIControlUndefined = -1,
kGUIButton = 1,
kGUILabel = 2,
kGUIInvWindow = 3,
kGUISlider = 4,
kGUITextBox = 5,
kGUIListBox = 6
};
// GUIControl general style and behavior flags
enum GUIControlFlags {
kGUICtrl_Default = 0x0001, // only button
kGUICtrl_Cancel = 0x0002, // unused
kGUICtrl_Enabled = 0x0004,
kGUICtrl_TabStop = 0x0008, // unused
kGUICtrl_Visible = 0x0010,
kGUICtrl_Clip = 0x0020, // only button
kGUICtrl_Clickable = 0x0040,
kGUICtrl_Translated = 0x0080, // 3.3.0.1132
kGUICtrl_Deleted = 0x8000, // unused (probably remains from the old editor?)
kGUICtrl_DefFlags = kGUICtrl_Enabled | kGUICtrl_Visible | kGUICtrl_Clickable | kGUICtrl_Translated,
// flags that had inverse meaning in old formats
kGUICtrl_OldFmtXorMask = kGUICtrl_Enabled | kGUICtrl_Visible | kGUICtrl_Clickable
};
// Label macro flags, define which macros are present in the Label's Text
enum GUILabelMacro {
kLabelMacro_None = 0,
kLabelMacro_Gamename = 0x01,
kLabelMacro_Overhotspot = 0x02,
kLabelMacro_Score = 0x04,
kLabelMacro_ScoreText = 0x08,
kLabelMacro_TotalScore = 0x10,
kLabelMacro_AllScore = kLabelMacro_Score | kLabelMacro_ScoreText,
kLabelMacro_All = 0xFFFF
};
// GUIListBox style and behavior flags
enum GUIListBoxFlags {
kListBox_ShowBorder = 0x01,
kListBox_ShowArrows = 0x02,
kListBox_SvgIndex = 0x04,
kListBox_DefFlags = kListBox_ShowBorder | kListBox_ShowArrows,
// flags that had inverse meaning in old formats
kListBox_OldFmtXorMask = kListBox_ShowBorder | kListBox_ShowArrows
};
// GUITextBox style and behavior flags
enum GUITextBoxFlags {
kTextBox_ShowBorder = 0x0001,
kTextBox_DefFlags = kTextBox_ShowBorder,
// flags that had inverse meaning in old formats
kTextBox_OldFmtXorMask = kTextBox_ShowBorder
};
// Savegame data format
// TODO: move to the engine code
enum GuiSvgVersion {
kGuiSvgVersion_Initial = 0,
kGuiSvgVersion_350,
kGuiSvgVersion_36020,
kGuiSvgVersion_36023,
kGuiSvgVersion_36025
};
// Style of GUI drawing in disabled state
enum GuiDisableStyle {
kGuiDis_Undefined = -1, // this is for marking not-disabled state
kGuiDis_Greyout = 0, // paint "gisabled" effect over controls
kGuiDis_Blackout = 1, // invisible controls (but guis are shown
kGuiDis_Unchanged = 2, // no change, display normally
kGuiDis_Off = 3 // fully invisible guis
};
// Global GUI options
struct GuiOptions {
// Clip GUI control's contents to the control's rectangle
bool ClipControls = true;
// How the GUI controls are drawn when the interface is disabled
GuiDisableStyle DisabledStyle = kGuiDis_Unchanged;
// Whether to graphically outline GUI controls
bool OutlineControls = false;
};
} // namespace Shared
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,128 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ags/shared/ac/game_version.h"
#include "ags/shared/gui/gui_inv.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
GUIInvWindow::GUIInvWindow() {
IsMouseOver = false;
CharId = -1;
ItemWidth = 40;
ItemHeight = 22;
ColCount = 0;
RowCount = 0;
TopItem = 0;
CalculateNumCells();
_scEventCount = 0;
}
void GUIInvWindow::OnMouseEnter() {
IsMouseOver = true;
}
void GUIInvWindow::OnMouseLeave() {
IsMouseOver = false;
}
void GUIInvWindow::OnMouseUp() {
if (IsMouseOver)
IsActivated = true;
}
void GUIInvWindow::OnResized() {
CalculateNumCells();
MarkChanged();
}
void GUIInvWindow::WriteToFile(Stream *out) const {
GUIObject::WriteToFile(out);
out->WriteInt32(CharId);
out->WriteInt32(ItemWidth);
out->WriteInt32(ItemHeight);
}
void GUIInvWindow::ReadFromFile(Stream *in, GuiVersion gui_version) {
GUIObject::ReadFromFile(in, gui_version);
if (gui_version >= kGuiVersion_unkn_109) {
CharId = in->ReadInt32();
ItemWidth = in->ReadInt32();
ItemHeight = in->ReadInt32();
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
TopItem = in->ReadInt32();
}
} else {
CharId = -1;
ItemWidth = 40;
ItemHeight = 22;
TopItem = 0;
}
if (_G(loaded_game_file_version) >= kGameVersion_270) {
// ensure that some items are visible
if (ItemWidth > _width)
ItemWidth = _width;
if (ItemHeight > _height)
ItemHeight = _height;
}
CalculateNumCells();
}
void GUIInvWindow::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
GUIObject::ReadFromSavegame(in, svg_ver);
ItemWidth = in->ReadInt32();
ItemHeight = in->ReadInt32();
CharId = in->ReadInt32();
TopItem = in->ReadInt32();
CalculateNumCells();
}
void GUIInvWindow::WriteToSavegame(Stream *out) const {
GUIObject::WriteToSavegame(out);
out->WriteInt32(ItemWidth);
out->WriteInt32(ItemHeight);
out->WriteInt32(CharId);
out->WriteInt32(TopItem);
}
void GUIInvWindow::CalculateNumCells() {
if (ItemWidth <= 0 || ItemHeight <= 0) {
ColCount = 0;
RowCount = 0;
} else if (_G(loaded_game_file_version) >= kGameVersion_270) {
ColCount = _width / data_to_game_coord(ItemWidth);
RowCount = _height / data_to_game_coord(ItemHeight);
} else {
ColCount = floor((float)_width / (float)data_to_game_coord(ItemWidth) + 0.5f);
RowCount = floor((float)_height / (float)data_to_game_coord(ItemHeight) + 0.5f);
}
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,74 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_INV_H
#define AGS_SHARED_GUI_GUI_INV_H
#include "common/std/vector.h"
#include "ags/shared/gui/gui_object.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
class GUIInvWindow : public GUIObject {
public:
GUIInvWindow();
bool HasAlphaChannel() const override;
// This function has distinct implementations in Engine and Editor
int GetCharacterId() const;
// Operations
// This function has distinct implementations in Engine and Editor
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
// Events
void OnMouseEnter() override;
void OnMouseLeave() override;
void OnMouseUp() override;
void OnResized() override;
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
void WriteToFile(Stream *out) const override;
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
void WriteToSavegame(Shared::Stream *out) const override;
// TODO: these members are currently public; hide them later
public:
bool IsMouseOver;
int32_t CharId; // whose inventory (-1 = current player)
int32_t ItemWidth;
int32_t ItemHeight;
int32_t ColCount;
int32_t RowCount;
int32_t TopItem;
private:
void CalculateNumCells();
};
} // namespace Shared
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,172 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/std/algorithm.h"
#include "ags/shared/ac/game_version.h"
#include "ags/shared/font/fonts.h"
#include "ags/shared/gui/gui_label.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/util/stream.h"
#include "ags/shared/util/string_utils.h"
#define GUILABEL_TEXTLENGTH_PRE272 200
namespace AGS3 {
namespace AGS {
namespace Shared {
GUILabel::GUILabel() {
Font = 0;
TextColor = 0;
TextAlignment = kHAlignLeft;
_scEventCount = 0;
}
bool GUILabel::HasAlphaChannel() const {
return is_font_antialiased(Font);
}
String GUILabel::GetText() const {
return Text;
}
GUILabelMacro GUILabel::GetTextMacros() const {
return _textMacro;
}
Rect GUILabel::CalcGraphicRect(bool clipped) {
if (clipped)
return RectWH(0, 0, _width, _height);
// TODO: need to find a way to text position, or there'll be some repetition
// have to precache text and size on some events:
// - translation change
// - macro value change (score, overhotspot etc)
Rect rc = RectWH(0, 0, _width, _height);
if (PrepareTextToDraw() == 0)
return rc;
const int linespacing = // Older engine labels used (font height + 1) as linespacing for some reason
((_G(loaded_game_file_version) < kGameVersion_360) && (get_font_flags(Font) & FFLG_DEFLINESPACING)) ?
(get_font_height(Font) + 1) :
get_font_linespacing(Font);
// < 2.72 labels did not limit vertical size of text
const bool limit_by_label_frame = _G(loaded_game_file_version) >= kGameVersion_272;
int at_y = 0;
Line max_line;
for (size_t i = 0;
i < _GP(Lines).Count() && (!limit_by_label_frame || at_y <= _height);
++i, at_y += linespacing) {
Line lpos = GUI::CalcTextPositionHor(_GP(Lines)[i].GetCStr(), Font, 0, 0 + _width - 1, at_y,
(FrameAlignment)TextAlignment);
max_line.X2 = MAX(max_line.X2, lpos.X2);
}
// Include font fixes for the first and last text line,
// in case graphical height is different, and there's a VerticalOffset
Line vextent = GUI::CalcFontGraphicalVExtent(Font);
Rect text_rc = RectWH(0, vextent.Y1, max_line.X2 - max_line.X1 + 1, at_y - linespacing + (vextent.Y2 - vextent.Y1));
return SumRects(rc, text_rc);
}
void GUILabel::Draw(Bitmap *ds, int x, int y) {
// TODO: need to find a way to cache text prior to drawing;
// but that will require to update all gui controls when translation is changed in game
if (PrepareTextToDraw() == 0)
return;
color_t text_color = ds->GetCompatibleColor(TextColor);
const int linespacing = // Older engine labels used (font height + 1) as linespacing for some reason
((_G(loaded_game_file_version) < kGameVersion_360) && (get_font_flags(Font) & FFLG_DEFLINESPACING)) ?
(get_font_height(Font) + 1) :
get_font_linespacing(Font);
// < 2.72 labels did not limit vertical size of text
const bool limit_by_label_frame = _G(loaded_game_file_version) >= kGameVersion_272;
int at_y = y;
for (size_t i = 0;
i < _GP(Lines).Count() && (!limit_by_label_frame || at_y <= y + _height);
++i, at_y += linespacing) {
GUI::DrawTextAlignedHor(ds, _GP(Lines)[i].GetCStr(), Font, text_color, x, x + _width - 1, at_y,
(FrameAlignment)TextAlignment);
}
}
void GUILabel::SetText(const String &text) {
if (text == Text)
return;
Text = text;
// Check for macros within text
_textMacro = GUI::FindLabelMacros(Text);
MarkChanged();
}
// TODO: replace string serialization with StrUtil::ReadString and WriteString
// methods in the future, to keep this organized.
void GUILabel::WriteToFile(Stream *out) const {
GUIObject::WriteToFile(out);
StrUtil::WriteString(Text, out);
out->WriteInt32(Font);
out->WriteInt32(TextColor);
out->WriteInt32(TextAlignment);
}
void GUILabel::ReadFromFile(Stream *in, GuiVersion gui_version) {
GUIObject::ReadFromFile(in, gui_version);
if (gui_version < kGuiVersion_272c)
Text.ReadCount(in, GUILABEL_TEXTLENGTH_PRE272);
else
Text = StrUtil::ReadString(in);
Font = in->ReadInt32();
TextColor = in->ReadInt32();
if (gui_version < kGuiVersion_350)
TextAlignment = ConvertLegacyGUIAlignment((LegacyGUIAlignment)in->ReadInt32());
else
TextAlignment = (HorAlignment)in->ReadInt32();
if (TextColor == 0)
TextColor = 16;
_textMacro = GUI::FindLabelMacros(Text);
}
void GUILabel::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
GUIObject::ReadFromSavegame(in, svg_ver);
Font = in->ReadInt32();
TextColor = in->ReadInt32();
Text = StrUtil::ReadString(in);
if (svg_ver >= kGuiSvgVersion_350)
TextAlignment = (HorAlignment)in->ReadInt32();
_textMacro = GUI::FindLabelMacros(Text);
}
void GUILabel::WriteToSavegame(Stream *out) const {
GUIObject::WriteToSavegame(out);
out->WriteInt32(Font);
out->WriteInt32(TextColor);
StrUtil::WriteString(Text, out);
out->WriteInt32(TextAlignment);
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,78 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_LABEL_H
#define AGS_SHARED_GUI_GUI_LABEL_H
#include "common/std/vector.h"
#include "ags/shared/gui/gui_object.h"
#include "ags/shared/util/string.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
class GUILabel : public GUIObject {
public:
GUILabel();
bool HasAlphaChannel() const override;
// Gets label's text property in original set form (with macros etc)
String GetText() const;
// Gets which macro are contained within label's text
GUILabelMacro GetTextMacros() const;
// Operations
Rect CalcGraphicRect(bool clipped) override;
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
void SetText(const String &text);
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
void WriteToFile(Stream *out) const override;
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
void WriteToSavegame(Shared::Stream *out) const override;
// TODO: these members are currently public; hide them later
public:
String Text;
int32_t Font;
color_t TextColor;
HorAlignment TextAlignment;
private:
// Transforms the Text property to a drawn text, applies translation,
// replaces macros, splits and wraps, etc; returns number of lines.
int PrepareTextToDraw();
// Information on macros contained within Text field
GUILabelMacro _textMacro;
// prepared text buffer/cache
// TODO: cache split lines instead?
String _textToDraw;
};
} // namespace Shared
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,453 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/std/algorithm.h"
#include "ags/shared/gui/gui_listbox.h"
#include "ags/shared/ac/game_version.h"
#include "ags/shared/font/fonts.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/util/stream.h"
#include "ags/shared/util/string_utils.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
GUIListBox::GUIListBox() {
ItemCount = 0;
SelectedItem = 0;
TopItem = 0;
RowHeight = 0;
VisibleItemCount = 0;
Font = 0;
TextColor = 0;
SelectedTextColor = 7;
ListBoxFlags = kListBox_DefFlags;
SelectedBgColor = 16;
TextAlignment = kHAlignLeft;
_scEventCount = 1;
_scEventNames[0] = "SelectionChanged";
_scEventArgs[0] = "GUIControl *control";
}
bool GUIListBox::HasAlphaChannel() const {
return is_font_antialiased(Font);
}
int GUIListBox::GetItemAt(int x, int y) const {
if (RowHeight <= 0 || IsInRightMargin(x))
return -1;
int index = y / RowHeight + TopItem;
if (index < 0 || index >= ItemCount)
return -1;
return index;
}
bool GUIListBox::AreArrowsShown() const {
return (ListBoxFlags & kListBox_ShowArrows) != 0;
}
bool GUIListBox::IsBorderShown() const {
return (ListBoxFlags & kListBox_ShowBorder) != 0;
}
bool GUIListBox::IsSvgIndex() const {
return (ListBoxFlags & kListBox_SvgIndex) != 0;
}
bool GUIListBox::IsInRightMargin(int x) const {
if (x >= (_width - get_fixed_pixel_size(6)) && IsBorderShown() && AreArrowsShown())
return 1;
return 0;
}
Rect GUIListBox::CalcGraphicRect(bool clipped) {
if (clipped)
return RectWH(0, 0, _width, _height);
// TODO: need to find a way to text position, or there'll be some repetition
// have to precache text and size on some events:
// - translation change
// - macro value change (score, overhotspot etc)
Rect rc = RectWH(0, 0, _width, _height);
UpdateMetrics();
const int width = _width - 1;
const int pixel_size = get_fixed_pixel_size(1);
int right_hand_edge = width - pixel_size - 1;
// calculate the scroll bar's width if necessary
if (ItemCount > VisibleItemCount &&IsBorderShown() && AreArrowsShown())
right_hand_edge -= get_fixed_pixel_size(7);
Line max_line;
for (int item = 0; (item < VisibleItemCount) && (item + TopItem < ItemCount); ++item) {
int at_y = pixel_size + item * RowHeight;
int item_index = item + TopItem;
PrepareTextToDraw(Items[item_index]);
Line lpos = GUI::CalcTextPositionHor(_textToDraw.GetCStr(), Font, 1 + pixel_size, right_hand_edge, at_y + 1,
(FrameAlignment)TextAlignment);
max_line.X2 = MAX(max_line.X2, lpos.X2);
}
int last_line_y = pixel_size + 1 + (VisibleItemCount - 1) * RowHeight;
// Include font fixes for the first and last text line,
// in case graphical height is different, and there's a VerticalOffset
Line vextent = GUI::CalcFontGraphicalVExtent(Font);
Rect text_rc = RectWH(0, vextent.Y1, max_line.X2 - max_line.X1 + 1, last_line_y + (vextent.Y2 - vextent.Y1));
return SumRects(rc, text_rc);
}
int GUIListBox::AddItem(const String &text) {
Items.push_back(text);
SavedGameIndex.push_back(-1);
ItemCount++;
MarkChanged();
return ItemCount - 1;
}
void GUIListBox::Clear() {
if (Items.empty())
return;
Items.clear();
SavedGameIndex.clear();
ItemCount = 0;
SelectedItem = 0;
TopItem = 0;
MarkChanged();
}
void GUIListBox::Draw(Bitmap *ds, int x, int y) {
const int width = _width - 1;
const int height = _height - 1;
const int pixel_size = get_fixed_pixel_size(1);
color_t text_color = ds->GetCompatibleColor(TextColor);
color_t draw_color = ds->GetCompatibleColor(TextColor);
if (IsBorderShown()) {
ds->DrawRect(Rect(x, y, x + width, y + height), draw_color);
if (pixel_size > 1)
ds->DrawRect(Rect(x + 1, y + 1, x + width - 1, y + height - 1), draw_color);
}
int right_hand_edge = (x + width) - pixel_size - 1;
// update the RowHeight and VisibleItemCount
// FIXME: find a way to update this whenever relevant things change in the engine
UpdateMetrics();
// draw the scroll bar in if necessary
bool scrollbar = (ItemCount > VisibleItemCount) && IsBorderShown() && AreArrowsShown();
if (scrollbar) {
int xstrt, ystrt;
ds->DrawRect(Rect(x + width - get_fixed_pixel_size(7), y, (x + (pixel_size - 1) + width) - get_fixed_pixel_size(7), y + height), draw_color);
ds->DrawRect(Rect(x + width - get_fixed_pixel_size(7), y + height / 2, x + width, y + height / 2 + (pixel_size - 1)), draw_color);
xstrt = (x + width - get_fixed_pixel_size(6)) + (pixel_size - 1);
ystrt = (y + height - 3) - get_fixed_pixel_size(5);
draw_color = ds->GetCompatibleColor(TextColor);
ds->DrawTriangle(Triangle(xstrt, ystrt, xstrt + get_fixed_pixel_size(4), ystrt,
xstrt + get_fixed_pixel_size(2),
ystrt + get_fixed_pixel_size(5)), draw_color);
ystrt = y + 3;
ds->DrawTriangle(Triangle(xstrt, ystrt + get_fixed_pixel_size(5),
xstrt + get_fixed_pixel_size(4),
ystrt + get_fixed_pixel_size(5),
xstrt + get_fixed_pixel_size(2), ystrt), draw_color);
right_hand_edge -= get_fixed_pixel_size(7);
}
Rect old_clip = ds->GetClip();
if (scrollbar && GUI::Options.ClipControls)
ds->SetClip(Rect(x, y, right_hand_edge + 1, y + _height - 1));
for (int item = 0; (item < VisibleItemCount) && (item + TopItem < ItemCount); ++item) {
int at_y = y + pixel_size + item * RowHeight;
if (item + TopItem == SelectedItem) {
text_color = ds->GetCompatibleColor(SelectedTextColor);
if (SelectedBgColor > 0) {
int stretch_to = (x + width) - pixel_size;
// draw the SelectedItem item bar (if colour not transparent)
draw_color = ds->GetCompatibleColor(SelectedBgColor);
if ((VisibleItemCount < ItemCount) && IsBorderShown() && AreArrowsShown())
stretch_to -= get_fixed_pixel_size(7);
ds->FillRect(Rect(x + pixel_size, at_y, stretch_to, at_y + RowHeight - pixel_size), draw_color);
}
} else
text_color = ds->GetCompatibleColor(TextColor);
int item_index = item + TopItem;
PrepareTextToDraw(Items[item_index]);
GUI::DrawTextAlignedHor(ds, _textToDraw.GetCStr(), Font, text_color, x + 1 + pixel_size, right_hand_edge, at_y + 1,
(FrameAlignment)TextAlignment);
}
ds->SetClip(old_clip);
}
int GUIListBox::InsertItem(int index, const String &text) {
if (index < 0 || index > ItemCount)
return -1;
Items.insert(Items.begin() + index, text);
SavedGameIndex.insert(SavedGameIndex.begin() + index, -1);
if (SelectedItem >= index)
SelectedItem++;
ItemCount++;
MarkChanged();
return ItemCount - 1;
}
void GUIListBox::RemoveItem(int index) {
if (index < 0 || index >= ItemCount)
return;
Items.erase(Items.begin() + index);
SavedGameIndex.erase(SavedGameIndex.begin() + index);
ItemCount--;
if (SelectedItem > index)
SelectedItem--;
if (SelectedItem >= ItemCount)
SelectedItem = -1;
MarkChanged();
}
void GUIListBox::SetShowArrows(bool on) {
if (on != ((ListBoxFlags & kListBox_ShowArrows) != 0))
MarkChanged();
if (on)
ListBoxFlags |= kListBox_ShowArrows;
else
ListBoxFlags &= ~kListBox_ShowArrows;
}
void GUIListBox::SetShowBorder(bool on) {
if (on != ((ListBoxFlags & kListBox_ShowBorder) != 0))
MarkChanged();
if (on)
ListBoxFlags |= kListBox_ShowBorder;
else
ListBoxFlags &= ~kListBox_ShowBorder;
}
void GUIListBox::SetSvgIndex(bool on) {
if (on)
ListBoxFlags |= kListBox_SvgIndex;
else
ListBoxFlags &= ~kListBox_SvgIndex;
}
void GUIListBox::SetFont(int font) {
if (Font == font)
return;
Font = font;
UpdateMetrics();
MarkChanged();
}
void GUIListBox::SetItemText(int index, const String &text) {
if ((index >= 0) && (index < ItemCount) && (text != Items[index])) {
Items[index] = text;
MarkChanged();
}
}
bool GUIListBox::OnMouseDown() {
if (IsInRightMargin(MousePos.X)) {
int top_item = TopItem;
if (MousePos.Y < _height / 2 && TopItem > 0)
top_item = TopItem - 1;
if (MousePos.Y >= _height / 2 && ItemCount > TopItem + VisibleItemCount)
top_item = TopItem + 1;
if (TopItem != top_item) {
TopItem = top_item;
MarkChanged();
}
return false;
}
int sel = GetItemAt(MousePos.X, MousePos.Y);
if (sel < 0)
return false;
if (sel != SelectedItem) {
SelectedItem = sel;
MarkChanged();
}
IsActivated = true;
return false;
}
void GUIListBox::OnMouseMove(int x_, int y_) {
MousePos.X = x_ - X;
MousePos.Y = y_ - Y;
}
void GUIListBox::OnResized() {
UpdateMetrics();
MarkChanged();
}
void GUIListBox::UpdateMetrics() {
int font_height = (_G(loaded_game_file_version) < kGameVersion_360_21) ?
get_font_height(Font) : get_font_height_outlined(Font);
RowHeight = font_height + get_fixed_pixel_size(2); // +1 top/bottom margin
VisibleItemCount = _height / RowHeight;
if (ItemCount <= VisibleItemCount)
TopItem = 0; // reset scroll if all items are visible
}
// TODO: replace string serialization with StrUtil::ReadString and WriteString
// methods in the future, to keep this organized.
void GUIListBox::WriteToFile(Stream *out) const {
GUIObject::WriteToFile(out);
out->WriteInt32(ItemCount);
out->WriteInt32(Font);
out->WriteInt32(TextColor);
out->WriteInt32(SelectedTextColor);
out->WriteInt32(ListBoxFlags);
out->WriteInt32(TextAlignment);
out->WriteInt32(SelectedBgColor);
for (int i = 0; i < ItemCount; ++i)
Items[i].Write(out);
}
void GUIListBox::ReadFromFile(Stream *in, GuiVersion gui_version) {
Clear();
GUIObject::ReadFromFile(in, gui_version);
ItemCount = in->ReadInt32();
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
SelectedItem = in->ReadInt32();
TopItem = in->ReadInt32();
MousePos.X = in->ReadInt32();
MousePos.Y = in->ReadInt32();
RowHeight = in->ReadInt32();
VisibleItemCount = in->ReadInt32();
}
Font = in->ReadInt32();
TextColor = in->ReadInt32();
SelectedTextColor = in->ReadInt32();
ListBoxFlags = in->ReadInt32();
// reverse particular flags from older format
if (gui_version < kGuiVersion_350)
ListBoxFlags ^= kListBox_OldFmtXorMask;
if (gui_version >= kGuiVersion_272b) {
if (gui_version < kGuiVersion_350) {
TextAlignment = ConvertLegacyGUIAlignment((LegacyGUIAlignment)in->ReadInt32());
in->ReadInt32(); // reserved1
} else {
TextAlignment = (HorAlignment)in->ReadInt32();
}
} else {
TextAlignment = kHAlignLeft;
}
if (gui_version >= kGuiVersion_unkn_107) {
SelectedBgColor = in->ReadInt32();
} else {
SelectedBgColor = TextColor;
if (SelectedBgColor == 0)
SelectedBgColor = 16;
}
// NOTE: we leave items in game data format as a potential support for defining
// ListBox contents at design-time, although Editor does not support it as of 3.5.0.
Items.resize(ItemCount);
SavedGameIndex.resize(ItemCount, -1);
for (int i = 0; i < ItemCount; ++i) {
Items[i].Read(in);
}
if (gui_version >= kGuiVersion_272d && gui_version < kGuiVersion_350 &&
(ListBoxFlags & kListBox_SvgIndex)) { // NOTE: reading into actual variables only for old savegame support
for (int i = 0; i < ItemCount; ++i)
SavedGameIndex[i] = in->ReadInt16();
}
if (TextColor == 0)
TextColor = 16;
UpdateMetrics();
}
void GUIListBox::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
GUIObject::ReadFromSavegame(in, svg_ver);
// Properties
ListBoxFlags = in->ReadInt32();
Font = in->ReadInt32();
if (svg_ver < kGuiSvgVersion_350) {
// reverse particular flags from older format
ListBoxFlags ^= kListBox_OldFmtXorMask;
} else {
SelectedBgColor = in->ReadInt32();
SelectedTextColor = in->ReadInt32();
TextAlignment = (HorAlignment)in->ReadInt32();
TextColor = in->ReadInt32();
}
// Items
ItemCount = in->ReadInt32();
Items.resize(ItemCount);
SavedGameIndex.resize(ItemCount);
for (int i = 0; i < ItemCount; ++i)
Items[i] = StrUtil::ReadString(in);
// TODO: investigate this, it might be unreasonable to save and read
// savegame index like that because list of savegames may easily change
// in between writing and restoring the game. Perhaps clearing and forcing
// this list to update on load somehow may make more sense.
if (ListBoxFlags & kListBox_SvgIndex)
for (int i = 0; i < ItemCount; ++i)
SavedGameIndex[i] = in->ReadInt16();
TopItem = in->ReadInt32();
SelectedItem = in->ReadInt32();
UpdateMetrics();
}
void GUIListBox::WriteToSavegame(Stream *out) const {
GUIObject::WriteToSavegame(out);
// Properties
out->WriteInt32(ListBoxFlags);
out->WriteInt32(Font);
out->WriteInt32(SelectedBgColor);
out->WriteInt32(SelectedTextColor);
out->WriteInt32(TextAlignment);
out->WriteInt32(TextColor);
// Items
out->WriteInt32(ItemCount);
for (int i = 0; i < ItemCount; ++i)
StrUtil::WriteString(Items[i], out);
if (ListBoxFlags & kListBox_SvgIndex)
for (int i = 0; i < ItemCount; ++i)
out->WriteInt16(SavedGameIndex[i]);
out->WriteInt32(TopItem);
out->WriteInt32(SelectedItem);
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,103 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_LISTBOX_H
#define AGS_SHARED_GUI_GUI_LISTBOX_H
#include "common/std/vector.h"
#include "ags/shared/gui/gui_object.h"
#include "ags/shared/util/string.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
class GUIListBox : public GUIObject {
public:
GUIListBox();
bool HasAlphaChannel() const override;
bool AreArrowsShown() const;
bool IsBorderShown() const;
bool IsSvgIndex() const;
bool IsInRightMargin(int x) const;
int GetItemAt(int x, int y) const;
// Operations
int AddItem(const String &text);
void Clear();
Rect CalcGraphicRect(bool clipped) override;
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
int InsertItem(int index, const String &text);
void RemoveItem(int index);
void SetShowArrows(bool on);
void SetShowBorder(bool on);
void SetSvgIndex(bool on); // TODO: work around this
void SetFont(int font);
void SetItemText(int index, const String &textt);
// Events
bool OnMouseDown() override;
void OnMouseMove(int x, int y) override;
void OnResized() override;
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
void WriteToFile(Stream *out) const override;
void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver) override;
void WriteToSavegame(Shared::Stream *out) const override;
// TODO: these members are currently public; hide them later
public:
int32_t Font;
color_t TextColor;
HorAlignment TextAlignment;
color_t SelectedBgColor;
color_t SelectedTextColor;
int32_t RowHeight;
int32_t VisibleItemCount;
std::vector<String> Items;
std::vector<int16_t> SavedGameIndex;
int32_t SelectedItem;
int32_t TopItem;
Point MousePos;
// TODO: remove these later
int32_t ItemCount;
private:
int32_t ListBoxFlags;
// Updates dynamic metrics such as row height and others
void UpdateMetrics();
// Applies translation
void PrepareTextToDraw(const String &text);
// prepared text buffer/cache
String _textToDraw;
};
} // namespace Shared
} // namespace AGS
} // namespace AGS3
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,301 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_MAIN_H
#define AGS_SHARED_GUI_GUI_MAIN_H
#include "common/std/vector.h"
#include "ags/engine/ac/draw.h"
#include "ags/shared/ac/common.h"
#include "ags/shared/ac/common_defines.h" // TODO: split out gui drawing helpers
#include "ags/shared/gfx/gfx_def.h" // TODO: split out gui drawing helpers
#include "ags/shared/gui/gui_defines.h"
#include "ags/shared/util/error.h"
#include "ags/shared/util/geometry.h"
#include "ags/shared/util/string.h"
namespace AGS3 {
// Forward declaration
namespace AGS {
namespace Shared {
class Stream;
}
}
using namespace AGS; // FIXME later
class SplitLines;
#define LEGACY_MAX_OBJS_ON_GUI 30
#define GUIMAIN_LEGACY_RESERVED_INTS 5
#define GUIMAIN_LEGACY_NAME_LENGTH 16
#define GUIMAIN_LEGACY_EVENTHANDLER_LENGTH 20
#define GUIMAIN_LEGACY_TW_FLAGS_SIZE 4
namespace AGS {
namespace Shared {
// Legacy GUIMain visibility state, which combined Visible property and override factor
enum LegacyGUIVisState {
kGUIVisibility_LockedOff = -1, // locked hidden (used by PopupMouseY guis)
kGUIVisibility_Off = 0, // hidden
kGUIVisibility_On = 1 // shown
};
class Bitmap;
class GUIObject;
class GUIMain {
public:
static String FixupGUIName(const String &name);
public:
GUIMain();
void InitDefaults();
// Tells if the gui background supports alpha channel
bool HasAlphaChannel() const;
// Tells if GUI will react on clicking on it
bool IsClickable() const { return (_flags & kGUIMain_Clickable) != 0; }
// Tells if GUI's visibility is overridden and it won't be displayed on
// screen regardless of Visible property (until concealed mode is off).
bool IsConcealed() const { return (_flags & kGUIMain_Concealed) != 0; }
// Tells if gui is actually meant to be displayed on screen.
// Normally Visible property determines whether GUI is allowed to be seen,
// but there may be other settings that override it.
bool IsDisplayed() const { return IsVisible() && !IsConcealed(); }
// Tells if given coordinates are within interactable area of gui
// NOTE: this currently tests for actual visibility and Clickable property
bool IsInteractableAt(int x, int y) const;
// Tells if gui is a text window
bool IsTextWindow() const { return (_flags & kGUIMain_TextWindow) != 0; }
// Tells if GUI is *allowed* to be displayed and interacted with.
// This does not necessarily mean that it is displayed right now, because
// GUI may be hidden for other reasons, including overriding behavior.
// For example GUI with kGUIPopupMouseY style will not be shown unless
// mouse cursor is at certain position on screen.
bool IsVisible() const { return (_flags & kGUIMain_Visible) != 0; }
// Tells if GUI has graphically changed recently
bool HasChanged() const { return _hasChanged; }
bool HasControlsChanged() const { return _hasControlsChanged; }
// Manually marks GUI as graphically changed
// NOTE: this only matters if GUI's own graphic changes (content, size etc),
// but not its state (visible) or texture drawing mode (transparency, etc).
void MarkChanged();
// Marks GUI as having any of its controls changed its looks.
void MarkControlChanged();
// Clears changed flag
void ClearChanged();
// Notify GUI about any of its controls changing its location.
void NotifyControlPosition();
// Notify GUI about one of its controls changing its interactive state.
void NotifyControlState(int objid, bool mark_changed);
// Resets control-under-mouse detection.
void ResetOverControl();
// Finds a control under given screen coordinates, returns control's child ID.
// Optionally allows extra leeway (offset in all directions) to let the user grab tiny controls.
// Optionally only allows clickable controls, ignoring non-clickable ones.
int32_t FindControlAt(int atx, int aty, int leeway = 0, bool must_be_clickable = true) const;
// Gets the number of the GUI child controls
int32_t GetControlCount() const;
// Gets control by its child's index
GUIObject *GetControl(int32_t index) const;
// Gets child control's type, looks up with child's index
GUIControlType GetControlType(int32_t index) const;
// Gets child control's global ID, looks up with child's index
int32_t GetControlID(int32_t index) const;
// Gets an array of child control indexes in the z-order, from bottom to top
const std::vector<int> &GetControlsDrawOrder() const;
// Child control management
// Note that currently GUIMain does not own controls (should not delete them)
void AddControl(GUIControlType type, int32_t id, GUIObject *control);
void RemoveAllControls();
// Operations
bool BringControlToFront(int32_t index);
void DrawSelf(Bitmap *ds);
void DrawWithControls(Bitmap *ds);
// Polls GUI state, providing current cursor (mouse) coordinates
void Poll(int mx, int my);
HError RebuildArray();
void ResortZOrder();
bool SendControlToBack(int32_t index);
// Sets whether GUI should react to player clicking on it
void SetClickable(bool on);
// Override GUI visibility; when in concealed mode GUI won't show up
// even if Visible = true
void SetConceal(bool on);
// Attempts to change control's zorder; returns if zorder changed
bool SetControlZOrder(int32_t index, int zorder);
// Changes GUI style to the text window or back
void SetTextWindow(bool on);
// Sets GUI transparency as a percentage (0 - 100) where 100 = invisible
void SetTransparencyAsPercentage(int percent);
// Sets whether GUI is allowed to be displayed on screen
void SetVisible(bool on);
// Events
void OnMouseButtonDown(int mx, int my);
void OnMouseButtonUp();
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version);
void WriteToFile(Stream *out) const;
// TODO: move to engine, into gui savegame component unit
// (should read/write GUI properties accessing them by interface)
void ReadFromSavegame(Stream *in, GuiSvgVersion svg_version);
void WriteToSavegame(Stream *out) const;
private:
void DrawBlob(Bitmap *ds, int x, int y, color_t draw_color);
// Same as FindControlAt but expects local space coordinates
int32_t FindControlAtLocal(int atx, int aty, int leeway, bool must_be_clickable) const;
// TODO: all members are currently public; hide them later
public:
int32_t ID; // GUI identifier
String Name; // the name of the GUI
int32_t X;
int32_t Y;
int32_t Width;
int32_t Height;
color_t BgColor; // background color
int32_t BgImage; // background sprite index
color_t FgColor; // foreground color (used as border color in normal GUIs,
// and text color in text windows)
int32_t Padding; // padding surrounding a GUI text window
GUIPopupStyle PopupStyle; // GUI popup behavior
int32_t PopupAtMouseY; // popup when _G(mousey) < this
int32_t Transparency; // "incorrect" alpha (in legacy 255-range units)
int32_t ZOrder;
int32_t FocusCtrl; // which control has the focus
int32_t HighlightCtrl; // which control has the bounding selection rect
int32_t MouseOverCtrl; // which control has the mouse cursor over it
int32_t MouseDownCtrl; // which control has the mouse button pressed on it
Point MouseWasAt; // last mouse cursor position
String OnClickHandler; // script function name
private:
int32_t _flags; // style and behavior flags
bool _hasChanged; // flag tells whether GUI has graphically changed recently
bool _hasControlsChanged;
bool _polling; // inside the polling process
// Array of types and control indexes in global GUI object arrays;
// maps GUI child slots to actual controls and used for rebuilding Controls array
typedef std::pair<GUIControlType, int32_t> ControlRef;
std::vector<ControlRef> _ctrlRefs;
// Array of child control references (not exclusively owned!)
std::vector<GUIObject *> _controls;
// Sorted array of controls in z-order.
std::vector<int> _ctrlDrawOrder;
};
namespace GUI {
extern GuiVersion GameGuiVersion;
extern GuiOptions Options;
// Applies current text direction setting (may depend on multiple factors)
String ApplyTextDirection(const String &text);
// Calculates the text's draw position, given the alignment
// optionally returns the real graphical rect that the text would occupy
Point CalcTextPosition(const char *text, int font, const Rect &frame, FrameAlignment align, Rect *gr_rect = nullptr);
// Calculates the text's draw position and horizontal extent,
// using strictly horizontal alignment
Line CalcTextPositionHor(const char *text, int font, int x1, int x2, int y, FrameAlignment align);
// Calculates the graphical rect that the text would occupy
// if drawn at the given coordinates
Rect CalcTextGraphicalRect(const char *text, int font, const Point &at);
// Calculates the graphical rect that the text would occupy
// if drawn aligned to the given frame
Rect CalcTextGraphicalRect(const char *text, int font, const Rect &frame, FrameAlignment align);
// Calculates a vertical graphical extent for a given font,
// which is a top and bottom offsets in zero-based coordinates.
// NOTE: this applies font size fixups.
Line CalcFontGraphicalVExtent(int font);
// Draw standart "shading" effect over rectangle
void DrawDisabledEffect(Bitmap *ds, const Rect &rc);
// Draw text aligned inside rectangle
void DrawTextAligned(Bitmap *ds, const char *text, int font, color_t text_color, const Rect &frame, FrameAlignment align);
// Draw text aligned horizontally inside given bounds
void DrawTextAlignedHor(Bitmap *ds, const char *text, int font, color_t text_color, int x1, int x2, int y, FrameAlignment align);
// Parses the string and returns combination of label macro flags
GUILabelMacro FindLabelMacros(const String &text);
// Applies text transformation necessary for rendering, in accordance to the
// current game settings, such as right-to-left render, and anything else
String TransformTextForDrawing(const String &text, bool translate, bool apply_direction);
// Wraps given text to make it fit into width, stores it in the lines;
// apply_direction param tells whether text direction setting should be applied
size_t SplitLinesForDrawing(const char *text, bool apply_direction, SplitLines &lines, int font, int width, size_t max_lines = -1);
// Mark all existing GUI for redraw
void MarkAllGUIForUpdate(bool redraw, bool reset_over_ctrl);
// Mark all translatable GUI controls for redraw
void MarkForTranslationUpdate();
// Mark all GUI which use the given font for recalculate/redraw;
// pass -1 to update all the textual controls together
void MarkForFontUpdate(int font);
// Mark labels that acts as special text placeholders for redraw
void MarkSpecialLabelsForUpdate(GUILabelMacro macro);
// Mark inventory windows for redraw, optionally only ones linked to given character;
// also marks buttons with inventory icon mode
void MarkInventoryForUpdate(int char_id, bool is_player);
// Reads all GUIs and their controls.
// WARNING: the data is read into the global arrays (guis, guibuts, and so on)
// TODO: remove is_savegame param after dropping support for old saves
// because only they use ReadGUI to read runtime GUI data
HError ReadGUI(Stream *in, bool is_savegame = false);
// Writes all GUIs and their controls.
// WARNING: the data is written from the global arrays (guis, guibuts, and so on)
void WriteGUI(Stream *out);
// Converts legacy GUIVisibility into appropriate GUIMain properties
void ApplyLegacyVisibility(GUIMain &gui, LegacyGUIVisState vis);
// Rebuilds GUIs, connecting them to the child controls in memory.
// WARNING: the data is processed in the global arrays (guis, guibuts, and so on)
HError RebuildGUI();
}
} // namespace Shared
} // namespace AGS
extern int get_adjusted_spritewidth(int spr);
extern int get_adjusted_spriteheight(int spr);
extern bool is_sprite_alpha(int spr);
extern void set_eip_guiobj(int eip);
extern int get_eip_guiobj();
} // namespace AGS3
#endif

View File

@@ -0,0 +1,223 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ags/shared/ac/common.h" // quit
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/gui/gui_object.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
GUIObject::GUIObject() {
Id = 0;
ParentId = 0;
Flags = kGUICtrl_DefFlags;
X = 0;
Y = 0;
_width = 0;
_height = 0;
ZOrder = -1;
IsActivated = false;
_transparency = 0;
_scEventCount = 0;
_hasChanged = true;
}
String GUIObject::GetScriptName() const {
return Name;
}
int GUIObject::GetEventCount() const {
return _scEventCount;
}
String GUIObject::GetEventName(int event) const {
if (event < 0 || event >= _scEventCount)
return "";
return _scEventNames[event];
}
String GUIObject::GetEventArgs(int event) const {
if (event < 0 || event >= _scEventCount)
return "";
return _scEventArgs[event];
}
bool GUIObject::IsOverControl(int x, int y, int leeway) const {
return x >= X && y >= Y && x < (X + _width + leeway) && y < (Y + _height + leeway);
}
void GUIObject::SetClickable(bool on) {
if (on != ((Flags & kGUICtrl_Clickable) != 0)) {
Flags = (Flags & ~kGUICtrl_Clickable) | kGUICtrl_Clickable * on;
MarkStateChanged(false, false); // update cursor-over-control only
}
}
void GUIObject::SetEnabled(bool on) {
if (on != ((Flags & kGUICtrl_Enabled) != 0)) {
Flags = (Flags & ~kGUICtrl_Enabled) | kGUICtrl_Enabled * on;
MarkStateChanged(true, true); // may change looks, and update cursor-over-control
}
}
void GUIObject::SetSize(int width, int height) {
if (_width != width || _height != height) {
_width = width;
_height = height;
OnResized();
}
}
void GUIObject::SetTranslated(bool on) {
if (on != ((Flags & kGUICtrl_Translated) != 0)) {
Flags = (Flags & ~kGUICtrl_Translated) | kGUICtrl_Translated * on;
MarkChanged();
}
}
void GUIObject::SetVisible(bool on) {
if (on != ((Flags & kGUICtrl_Visible) != 0)) {
Flags = (Flags & ~kGUICtrl_Visible) | kGUICtrl_Visible * on;
MarkStateChanged(false, true); // for software mode, and to update cursor-over-control
}
}
void GUIObject::SetTransparency(int trans) {
if (_transparency != trans) {
_transparency = trans;
MarkParentChanged(); // for software mode
}
}
// TODO: replace string serialization with StrUtil::ReadString and WriteString
// methods in the future, to keep this organized.
void GUIObject::WriteToFile(Stream *out) const {
out->WriteInt32(Flags);
out->WriteInt32(X);
out->WriteInt32(Y);
out->WriteInt32(_width);
out->WriteInt32(_height);
out->WriteInt32(ZOrder);
Name.Write(out);
out->WriteInt32(_scEventCount);
for (int i = 0; i < _scEventCount; ++i)
EventHandlers[i].Write(out);
}
void GUIObject::ReadFromFile(Stream *in, GuiVersion gui_version) {
Flags = in->ReadInt32();
// reverse particular flags from older format
if (gui_version < kGuiVersion_350)
Flags ^= kGUICtrl_OldFmtXorMask;
X = in->ReadInt32();
Y = in->ReadInt32();
_width = in->ReadInt32();
_height = in->ReadInt32();
ZOrder = in->ReadInt32();
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
IsActivated = in->ReadInt32() != 0;
}
if (gui_version >= kGuiVersion_unkn_106)
Name.Read(in);
else
Name.Free();
for (int i = 0; i < _scEventCount; ++i) {
EventHandlers[i].Free();
}
if (gui_version >= kGuiVersion_unkn_108) {
int evt_count = in->ReadInt32();
if (evt_count > _scEventCount)
quit("Error: too many control events, need newer version");
for (int i = 0; i < evt_count; ++i) {
EventHandlers[i].Read(in);
}
}
}
void GUIObject::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
// Properties
Flags = in->ReadInt32();
// reverse particular flags from older format
if (svg_ver < kGuiSvgVersion_350)
Flags ^= kGUICtrl_OldFmtXorMask;
X = in->ReadInt32();
Y = in->ReadInt32();
_width = in->ReadInt32();
_height = in->ReadInt32();
ZOrder = in->ReadInt32();
// Dynamic state
IsActivated = in->ReadBool() ? 1 : 0;
if (svg_ver >= kGuiSvgVersion_36023) {
_transparency = in->ReadInt32();
in->ReadInt32(); // reserve 3 ints
in->ReadInt32();
in->ReadInt32();
}
}
void GUIObject::WriteToSavegame(Stream *out) const {
// Properties
out->WriteInt32(Flags);
out->WriteInt32(X);
out->WriteInt32(Y);
out->WriteInt32(_width);
out->WriteInt32(_height);
out->WriteInt32(ZOrder);
// Dynamic state
out->WriteBool(IsActivated != 0);
out->WriteInt32(_transparency);
out->WriteInt32(0); // reserve 3 ints
out->WriteInt32(0);
out->WriteInt32(0);
}
HorAlignment ConvertLegacyGUIAlignment(LegacyGUIAlignment align) {
switch (align) {
case kLegacyGUIAlign_Right:
return kHAlignRight;
case kLegacyGUIAlign_Center:
return kHAlignCenter;
default:
return kHAlignLeft;
}
}
LegacyGUIAlignment GetLegacyGUIAlignment(HorAlignment align) {
switch (align) {
case kHAlignRight:
return kLegacyGUIAlign_Right;
case kHAlignCenter:
return kLegacyGUIAlign_Center;
default:
return kLegacyGUIAlign_Left;
}
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,172 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_OBJECT_H
#define AGS_SHARED_GUI_GUI_OBJECT_H
#include "ags/shared/core/types.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/gui/gui_defines.h"
#include "ags/shared/util/string.h"
#include "ags/globals.h"
namespace AGS3 {
struct KeyInput;
namespace AGS {
namespace Shared {
enum LegacyGUIAlignment {
kLegacyGUIAlign_Left = 0,
kLegacyGUIAlign_Right = 1,
kLegacyGUIAlign_Center = 2
};
class GUIObject {
public:
GUIObject();
virtual ~GUIObject() {}
String GetScriptName() const;
String GetEventArgs(int event) const;
int GetEventCount() const;
String GetEventName(int event) const;
bool IsClickable() const { return (Flags & kGUICtrl_Clickable) != 0; }
bool IsDeleted() const { return (Flags & kGUICtrl_Deleted) != 0; }
bool IsEnabled() const { return (Flags & kGUICtrl_Enabled) != 0; }
bool IsTranslated() const { return (Flags & kGUICtrl_Translated) != 0; }
bool IsVisible() const { return (Flags & kGUICtrl_Visible) != 0; }
// overridable routine to determine whether the mouse is over the control
virtual bool IsOverControl(int x, int y, int leeway) const;
Size GetSize() const { return Size(_width, _height); }
int GetWidth() const { return _width; }
int GetHeight() const { return _height; }
int GetTransparency() const { return _transparency; }
// Compatibility: should the control's graphic be clipped to its x,y,w,h
virtual bool IsContentClipped() const { return true; }
// Tells if the object image supports alpha channel
virtual bool HasAlphaChannel() const { return false; }
// Operations
// Returns the (untransformed!) visual rectangle of this control,
// in *relative* coordinates, optionally clipped by the logical size
virtual Rect CalcGraphicRect(bool /*clipped*/) {
return RectWH(0, 0, _width, _height);
}
virtual void Draw(Bitmap *ds, int x = 0, int y = 0) {
(void)ds; (void)x; (void)y;
}
void SetClickable(bool on);
void SetEnabled(bool on);
void SetSize(int width, int height);
inline void SetWidth(int width) { SetSize(width, _height); }
inline void SetHeight(int height) { SetSize(_width, height); }
void SetTranslated(bool on);
void SetVisible(bool on);
void SetTransparency(int trans);
// Events
// Key pressed for control
virtual void OnKeyPress(const KeyInput &) {}
// Mouse button down - return 'True' to lock focus
virtual bool OnMouseDown() {
return false;
}
// Mouse moves onto control
virtual void OnMouseEnter() {
}
// Mouse moves off control
virtual void OnMouseLeave() {
}
// Mouse moves over control - x,y relative to gui
virtual void OnMouseMove(int /*x*/, int /*y*/) {
}
// Mouse button up
virtual void OnMouseUp() {
}
// Control was resized
virtual void OnResized() { MarkPositionChanged(true); }
// Serialization
virtual void ReadFromFile(Shared::Stream *in, GuiVersion gui_version);
virtual void WriteToFile(Shared::Stream *out) const;
virtual void ReadFromSavegame(Shared::Stream *in, GuiSvgVersion svg_ver);
virtual void WriteToSavegame(Shared::Stream *out) const;
// TODO: these members are currently public; hide them later
public:
// Manually marks GUIObject as graphically changed
// NOTE: this only matters if control's own graphic changes, but not its
// logical (visible, clickable, etc) or visual (e.g. transparency) state.
void MarkChanged();
// Notifies parent GUI that this control has changed its visual state
void MarkParentChanged();
// Notifies parent GUI that this control has changed its location (pos, size)
void MarkPositionChanged(bool self_changed);
// Notifies parent GUI that this control's interactive state has changed
void MarkStateChanged(bool self_changed, bool parent_changed);
bool HasChanged() const { return _hasChanged; };
void ClearChanged();
int32_t Id; // GUI object's identifier
int32_t ParentId; // id of parent GUI
String Name; // script name
int32_t X;
int32_t Y;
int32_t ZOrder;
bool IsActivated; // signals user interaction
String EventHandlers[MAX_GUIOBJ_EVENTS]; // script function names
protected:
uint32_t Flags; // generic style and behavior flags
int32_t _width;
int32_t _height;
int32_t _transparency; // "incorrect" alpha (in legacy 255-range units)
bool _hasChanged;
// TODO: explicit event names & handlers for every event
// FIXME: these must be static!! per type
int32_t _scEventCount; // number of supported script events
String _scEventNames[MAX_GUIOBJ_EVENTS]; // script event names
String _scEventArgs[MAX_GUIOBJ_EVENTS]; // script handler params
};
// Converts legacy alignment type used in GUI Label/ListBox data (only left/right/center)
HorAlignment ConvertLegacyGUIAlignment(LegacyGUIAlignment align);
LegacyGUIAlignment GetLegacyGUIAlignment(HorAlignment align);
} // namespace Shared
} // namespace AGS
// Tells if all controls are disabled
// Tells if the given control is considered enabled, taking global flag into account
inline bool IsGUIEnabled(AGS::Shared::GUIObject *g) {
return (_G(all_buttons_disabled) < 0) && g->IsEnabled();
}
} // namespace AGS3
#endif

View File

@@ -0,0 +1,284 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/std/algorithm.h"
#include "ags/shared/ac/sprite_cache.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/gui/gui_slider.h"
#include "ags/shared/util/stream.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
GUISlider::GUISlider() {
MinValue = 0;
MaxValue = 10;
Value = 0;
BgImage = 0;
HandleImage = 0;
HandleOffset = 0;
IsMousePressed = false;
_scEventCount = 1;
_scEventNames[0] = "Change";
_scEventArgs[0] = "GUIControl *control";
_handleRange = 0;
}
bool GUISlider::IsHorizontal() const {
return _width > _height;
}
bool GUISlider::HasAlphaChannel() const {
return is_sprite_alpha(BgImage) || is_sprite_alpha(HandleImage);
}
bool GUISlider::IsOverControl(int x, int y, int leeway) const {
// check the overall boundary
if (GUIObject::IsOverControl(x, y, leeway))
return true;
// now check the handle too
return _cachedHandle.IsInside(Point(x - X, y - Y));
}
Rect GUISlider::CalcGraphicRect(bool /*clipped*/) {
// Sliders are never clipped as of 3.6.0
// TODO: precalculate everything on width/height/graphic change!!
UpdateMetrics();
Rect logical = RectWH(0, 0, _width, _height);
Rect bar = _cachedBar;
Rect handle = _cachedHandle;
return Rect(
MIN(MIN(logical.Left, bar.Left), handle.Left),
MIN(MIN(logical.Top, bar.Top), handle.Top),
MAX(MAX(logical.Right, bar.Right), handle.Right),
MAX(MAX(logical.Bottom, bar.Bottom), handle.Bottom)
);
}
void GUISlider::UpdateMetrics() {
// Clamp Value
// TODO: this is necessary here because some Slider fields are still public
if (MinValue >= MaxValue)
MaxValue = MinValue + 1;
Value = Math::Clamp(Value, MinValue, MaxValue);
// Test if sprite is available; // TODO: return a placeholder from spriteset instead!
const int handle_im = ((HandleImage > 0) && _GP(spriteset).DoesSpriteExist(HandleImage)) ? HandleImage : 0;
// Depending on slider's orientation, thickness is either Height or Width
const int thickness = IsHorizontal() ? _height : _width;
// "thick_f" is the factor for calculating relative element positions
const int thick_f = thickness / 3; // one third of the control's thickness
// Bar thickness
const int bar_thick = thick_f * 2 + 2;
// Calculate handle size
Size handle_sz;
if (handle_im > 0) // handle is a sprite
{
handle_sz = Size(get_adjusted_spritewidth(handle_im),
get_adjusted_spriteheight(handle_im));
} else // handle is a drawn rectangle
{
if (IsHorizontal())
handle_sz = Size(get_fixed_pixel_size(4) + 1, bar_thick + (thick_f - 1) * 2);
else
handle_sz = Size(bar_thick + (thick_f - 1) * 2, get_fixed_pixel_size(4) + 1);
}
// Calculate bar and handle positions
Rect bar;
Rect handle;
int handle_range;
if (IsHorizontal()) // horizontal slider
{
// Value pos is a coordinate corresponding to current slider's value
bar = RectWH(1, _height / 2 - thick_f, _width - 1, bar_thick);
handle_range = _width - 4;
int value_pos = (int)(((float)(Value - MinValue) * (float)handle_range) / (float)(MaxValue - MinValue));
handle = RectWH((bar.Left + get_fixed_pixel_size(2)) - (handle_sz.Width / 2) + 1 + value_pos - 2,
bar.Top + (bar.GetHeight() - handle_sz.Height) / 2,
handle_sz.Width, handle_sz.Height);
handle.MoveToY(handle.Top + data_to_game_coord(HandleOffset));
}
// vertical slider
else {
bar = RectWH(_width / 2 - thick_f, 1, bar_thick, _height - 1);
handle_range = _height - 4;
int value_pos = (int)(((float)(MaxValue - Value) * (float)handle_range) / (float)(MaxValue - MinValue));
handle = RectWH(bar.Left + (bar.GetWidth() - handle_sz.Width) / 2,
(bar.Top + get_fixed_pixel_size(2)) - (handle_sz.Height / 2) + 1 + value_pos - 2,
handle_sz.Width, handle_sz.Height);
handle.MoveToX(handle.Left + data_to_game_coord(HandleOffset));
}
_cachedBar = bar;
_cachedHandle = handle;
_handleRange = MAX(1, handle_range);
}
void GUISlider::Draw(Bitmap *ds, int x, int y) {
UpdateMetrics();
Rect bar = Rect::MoveBy(_cachedBar, x, y);
Rect handle = Rect::MoveBy(_cachedHandle, x, y);
color_t draw_color;
if (BgImage > 0) {
// tiled image as slider background
int x_inc = 0;
int y_inc = 0;
if (IsHorizontal()) {
x_inc = get_adjusted_spritewidth(BgImage);
// centre the image vertically
bar.Top = y + (_height / 2) - get_adjusted_spriteheight(BgImage) / 2;
} else {
y_inc = get_adjusted_spriteheight(BgImage);
// centre the image horizontally
bar.Left = x + (_width / 2) - get_adjusted_spritewidth(BgImage) / 2;
}
int cx = bar.Left;
int cy = bar.Top;
// draw the tiled background image
do {
draw_gui_sprite(ds, BgImage, cx, cy, true);
cx += x_inc;
cy += y_inc;
// done as a do..while so that at least one of the image is drawn
} while ((cx + x_inc <= bar.Right) && (cy + y_inc <= bar.Bottom));
} else {
// normal grey background
draw_color = ds->GetCompatibleColor(16);
ds->FillRect(bar, draw_color);
draw_color = ds->GetCompatibleColor(8);
ds->DrawLine(Line(bar.Left, bar.Top, bar.Left, bar.Bottom), draw_color);
ds->DrawLine(Line(bar.Left, bar.Top, bar.Right, bar.Top), draw_color);
draw_color = ds->GetCompatibleColor(15);
ds->DrawLine(Line(bar.Right, bar.Top + 1, bar.Right, bar.Bottom), draw_color);
ds->DrawLine(Line(bar.Left, bar.Bottom, bar.Right, bar.Bottom), draw_color);
}
// Test if sprite is available; // TODO: return a placeholder from spriteset instead!
const int handle_im = ((HandleImage > 0) && _GP(spriteset).DoesSpriteExist(HandleImage)) ? HandleImage : 0;
if (handle_im > 0) // handle is a sprite
{
draw_gui_sprite(ds, handle_im, handle.Left, handle.Top, true);
} else // handle is a drawn rectangle
{
// normal grey tracker handle
draw_color = ds->GetCompatibleColor(7);
ds->FillRect(handle, draw_color);
draw_color = ds->GetCompatibleColor(15);
ds->DrawLine(Line(handle.Left, handle.Top, handle.Right, handle.Top), draw_color);
ds->DrawLine(Line(handle.Left, handle.Top, handle.Left, handle.Bottom), draw_color);
draw_color = ds->GetCompatibleColor(16);
ds->DrawLine(Line(handle.Right, handle.Top + 1, handle.Right, handle.Bottom), draw_color);
ds->DrawLine(Line(handle.Left + 1, handle.Bottom, handle.Right, handle.Bottom), draw_color);
}
}
bool GUISlider::OnMouseDown() {
IsMousePressed = true;
// lock focus to ourselves
return true;
}
void GUISlider::OnMouseMove(int x, int y) {
if (!IsMousePressed)
return;
int32_t value;
assert(_handleRange > 0);
if (IsHorizontal())
value = (int)(((float)((x - X) - 2) * (float)(MaxValue - MinValue)) / (float)_handleRange) + MinValue;
else
value = (int)(((float)(((Y + _height) - y) - 2) * (float)(MaxValue - MinValue)) / (float)_handleRange) + MinValue;
value = Math::Clamp(value, MinValue, MaxValue);
if (value != Value) {
Value = value;
MarkChanged();
}
IsActivated = true;
}
void GUISlider::OnMouseUp() {
IsMousePressed = false;
}
void GUISlider::ReadFromFile(Stream *in, GuiVersion gui_version) {
GUIObject::ReadFromFile(in, gui_version);
MinValue = in->ReadInt32();
MaxValue = in->ReadInt32();
Value = in->ReadInt32();
if (gui_version < kGuiVersion_350) { // NOTE: reading into actual variables only for old savegame support
IsMousePressed = in->ReadInt32() != 0;
}
if (gui_version >= kGuiVersion_unkn_104) {
HandleImage = in->ReadInt32();
HandleOffset = in->ReadInt32();
BgImage = in->ReadInt32();
} else {
HandleImage = -1;
HandleOffset = 0;
BgImage = 0;
}
UpdateMetrics();
}
void GUISlider::WriteToFile(Stream *out) const {
GUIObject::WriteToFile(out);
out->WriteInt32(MinValue);
out->WriteInt32(MaxValue);
out->WriteInt32(Value);
out->WriteInt32(HandleImage);
out->WriteInt32(HandleOffset);
out->WriteInt32(BgImage);
}
void GUISlider::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
GUIObject::ReadFromSavegame(in, svg_ver);
BgImage = in->ReadInt32();
HandleImage = in->ReadInt32();
HandleOffset = in->ReadInt32();
MinValue = in->ReadInt32();
MaxValue = in->ReadInt32();
Value = in->ReadInt32();
UpdateMetrics();
}
void GUISlider::WriteToSavegame(Stream *out) const {
GUIObject::WriteToSavegame(out);
out->WriteInt32(BgImage);
out->WriteInt32(HandleImage);
out->WriteInt32(HandleOffset);
out->WriteInt32(MinValue);
out->WriteInt32(MaxValue);
out->WriteInt32(Value);
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,85 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_SLIDER_H
#define AGS_SHARED_GUI_GUI_SLIDER_H
#include "common/std/vector.h"
#include "ags/shared/gui/gui_object.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
class GUISlider : public GUIObject {
public:
GUISlider();
// Tells if the slider is horizontal (otherwise - vertical)
bool IsHorizontal() const;
bool IsOverControl(int x, int y, int leeway) const override;
// Compatibility: sliders are not clipped as of 3.6.0
bool IsContentClipped() const override { return false; }
bool HasAlphaChannel() const override;
// Operations
Rect CalcGraphicRect(bool clipped) override;
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
// Events
bool OnMouseDown() override;
void OnMouseMove(int xp, int yp) override;
void OnMouseUp() override;
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
void WriteToFile(Stream *out) const override;
void ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) override;
void WriteToSavegame(Stream *out) const override;
// TODO: these members are currently public; hide them later
public:
int32_t MinValue;
int32_t MaxValue;
int32_t Value;
int32_t BgImage;
int32_t HandleImage;
int32_t HandleOffset;
bool IsMousePressed;
private:
// Updates dynamic metrics and positions of elements
void UpdateMetrics();
// Cached coordinates of slider bar; in relative coords
Rect _cachedBar;
// Cached coordinates of slider handle; in relative coords
Rect _cachedHandle;
// The length of the handle movement range, in pixels
int _handleRange;
};
} // namespace Shared
} // namespace AGS
} // namespace AGS3
#endif

View File

@@ -0,0 +1,177 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ags/shared/ac/keycode.h"
#include "ags/shared/font/fonts.h"
#include "ags/shared/gui/gui_main.h"
#include "ags/shared/gui/gui_textbox.h"
#include "ags/shared/util/stream.h"
#include "ags/shared/util/string_utils.h"
namespace AGS3 {
#define GUITEXTBOX_LEGACY_TEXTLEN 200
namespace AGS {
namespace Shared {
GUITextBox::GUITextBox() {
Font = 0;
TextColor = 0;
TextBoxFlags = kTextBox_DefFlags;
_scEventCount = 1;
_scEventNames[0] = "Activate";
_scEventArgs[0] = "GUIControl *control";
}
bool GUITextBox::HasAlphaChannel() const {
return is_font_antialiased(Font);
}
bool GUITextBox::IsBorderShown() const {
return (TextBoxFlags & kTextBox_ShowBorder) != 0;
}
Rect GUITextBox::CalcGraphicRect(bool clipped) {
if (clipped)
return RectWH(0, 0, _width, _height);
// TODO: need to find a way to cache text position, or there'll be some repetition
Rect rc = RectWH(0, 0, _width, _height);
Point text_at(1 + get_fixed_pixel_size(1), 1 + get_fixed_pixel_size(1));
Rect text_rc = GUI::CalcTextGraphicalRect(Text.GetCStr(), Font, text_at);
if (IsGUIEnabled(this)) {
// add a cursor
Rect cur_rc = RectWH(
text_rc.Right + 3,
1 + get_font_height(Font),
get_fixed_pixel_size(5),
get_fixed_pixel_size(1) - 1);
text_rc = SumRects(text_rc, cur_rc);
}
return SumRects(rc, text_rc);
}
void GUITextBox::Draw(Bitmap *ds, int x, int y) {
color_t text_color = ds->GetCompatibleColor(TextColor);
color_t draw_color = ds->GetCompatibleColor(TextColor);
if (IsBorderShown()) {
ds->DrawRect(RectWH(x, y, _width, _height), draw_color);
if (get_fixed_pixel_size(1) > 1) {
ds->DrawRect(Rect(x + 1, y + 1, x + _width - get_fixed_pixel_size(1), y + _height - get_fixed_pixel_size(1)), draw_color);
}
}
DrawTextBoxContents(ds, x, y, text_color);
}
// TODO: a shared utility function
static void Backspace(String &text) {
if (get_uformat() == U_UTF8) {// Find where the last utf8 char begins
const char *ptr_end = text.GetCStr() + text.GetLength();
const char *ptr = ptr_end - 1;
for (; ptr > text.GetCStr() && ((*ptr & 0xC0) == 0x80); --ptr);
text.ClipRight(ptr_end - ptr);
} else {
text.ClipRight(1);
}
}
void GUITextBox::OnKeyPress(const KeyInput &ki) {
switch (ki.Key) {
case eAGSKeyCodeReturn:
IsActivated = true;
return;
case eAGSKeyCodeBackspace:
Backspace(Text);
MarkChanged();
return;
default: break;
}
if (ki.UChar == 0)
return; // not a textual event
if (get_uformat() == U_UTF8)
Text.Append(ki.Text); // proper unicode char
else if (ki.UChar < 256)
Text.AppendChar(static_cast<uint8_t>(ki.UChar)); // ascii/ansi-range char in ascii mode
else
return; // char from an unsupported range, don't print but still report as handled
// if the new string is too long, remove the new character
if (get_text_width(Text.GetCStr(), Font) > (_width - (6 + get_fixed_pixel_size(5))))
Backspace(Text);
MarkChanged();
}
void GUITextBox::SetShowBorder(bool on) {
if (on)
TextBoxFlags |= kTextBox_ShowBorder;
else
TextBoxFlags &= ~kTextBox_ShowBorder;
}
// TODO: replace string serialization with StrUtil::ReadString and WriteString
// methods in the future, to keep this organized.
void GUITextBox::WriteToFile(Stream *out) const {
GUIObject::WriteToFile(out);
StrUtil::WriteString(Text, out);
out->WriteInt32(Font);
out->WriteInt32(TextColor);
out->WriteInt32(TextBoxFlags);
}
void GUITextBox::ReadFromFile(Stream *in, GuiVersion gui_version) {
GUIObject::ReadFromFile(in, gui_version);
if (gui_version < kGuiVersion_350)
Text.ReadCount(in, GUITEXTBOX_LEGACY_TEXTLEN);
else
Text = StrUtil::ReadString(in);
Font = in->ReadInt32();
TextColor = in->ReadInt32();
TextBoxFlags = in->ReadInt32();
// reverse particular flags from older format
if (gui_version < kGuiVersion_350)
TextBoxFlags ^= kTextBox_OldFmtXorMask;
if (TextColor == 0)
TextColor = 16;
}
void GUITextBox::ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) {
GUIObject::ReadFromSavegame(in, svg_ver);
Font = in->ReadInt32();
TextColor = in->ReadInt32();
Text = StrUtil::ReadString(in);
if (svg_ver >= kGuiSvgVersion_350)
TextBoxFlags = in->ReadInt32();
}
void GUITextBox::WriteToSavegame(Stream *out) const {
GUIObject::WriteToSavegame(out);
out->WriteInt32(Font);
out->WriteInt32(TextColor);
StrUtil::WriteString(Text, out);
out->WriteInt32(TextBoxFlags);
}
} // namespace Shared
} // namespace AGS
} // namespace AGS3

View File

@@ -0,0 +1,71 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGS_SHARED_GUI_GUI_TEXTBOX_H
#define AGS_SHARED_GUI_GUI_TEXTBOX_H
#include "common/std/vector.h"
#include "ags/shared/gui/gui_object.h"
#include "ags/shared/util/string.h"
namespace AGS3 {
namespace AGS {
namespace Shared {
class GUITextBox : public GUIObject {
public:
GUITextBox();
bool HasAlphaChannel() const override;
bool IsBorderShown() const;
// Operations
Rect CalcGraphicRect(bool clipped) override;
void Draw(Bitmap *ds, int x = 0, int y = 0) override;
void SetShowBorder(bool on);
// Events
void OnKeyPress(const KeyInput &ki) override;
// Serialization
void ReadFromFile(Stream *in, GuiVersion gui_version) override;
void WriteToFile(Stream *out) const override;
void ReadFromSavegame(Stream *in, GuiSvgVersion svg_ver) override;
void WriteToSavegame(Stream *out) const override;
// TODO: these members are currently public; hide them later
public:
int32_t Font;
String Text;
color_t TextColor;
private:
int32_t TextBoxFlags;
String _textToDraw;
void DrawTextBoxContents(Bitmap *ds, int x, int y, color_t text_color);
};
} // namespace Shared
} // namespace AGS
} // namespace AGS3
#endif