Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
import dialogs.Dialog;
import elements.GuiButton;
import elements.GuiText;
import elements.GuiTextbox;
import elements.GuiSpinbox;
import elements.GuiScrollbar;
interface ColorDialogCallback {
void colorChosen(Color col);
};
class ColorPicker : BaseGuiElement {
float hue = 1.f, sat = 1.f, value = 1.f, alpha = 1.f;
Color picked;
bool pressed = false;
ColorPicker(IGuiElement@ parent, const recti& pos) {
super(parent, pos);
}
void draw() {
shader::HSV_VALUE = value;
shader::HSV_SAT_START = 0.f;
shader::HSV_SAT_END = 1.f;
drawRectangle(AbsolutePosition, material::HSVPalette, Color());
BaseGuiElement::draw();
}
Color get_color() const {
Colorf col;
col.fromHSV(hue, sat, value);
col.a = alpha;
return Color(col);
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
if(event.type == MET_Button_Down || (event.type == MET_Moved && pressed)) {
pressed = true;
hue = float(event.x - AbsolutePosition.topLeft.x);
sat = 1.f - float(event.y - AbsolutePosition.topLeft.y) / float(AbsolutePosition.height);
picked = color;
GuiEvent evt;
@evt.caller = this;
evt.type = GUI_Changed;
onGuiEvent(evt);
return true;
}
else if(pressed && event.type == MET_Button_Up) {
pressed = false;
return true;
}
return BaseGuiElement::onMouseEvent(event, source);
}
};
class ColorBox : BaseGuiElement {
Color color;
ColorBox(IGuiElement@ parent, const recti& pos) {
super(parent, pos);
}
void draw() {
drawRectangle(AbsolutePosition, color);
BaseGuiElement::draw();
}
};
class ColorDialog : Dialog {
ColorDialogCallback@ callback;
ColorPicker@ picker;
GuiScrollbar@ value;
ColorBox@ display;
GuiButton@ accept;
GuiSpinbox@ r, g, b, a;
ColorDialog(ColorDialogCallback@ CB, IGuiElement@ bind, Color startCol = Color()) {
@callback = CB;
super(bind);
height = 330;
width = 480;
@picker = ColorPicker(window, recti_area(vec2i(6,6), vec2i(360,256)));
@accept = GuiButton(window, recti(400, 300, 476, 326), locale::ACCEPT);
@value = GuiScrollbar(window, recti(370, 6, 396, 280));
value.page = 0;
value.scroll = 5.f / 255.f;
@r = GuiSpinbox(window, recti(400, 6, 450, 32), 0, 0, 255, 5, 0);
@b = GuiSpinbox(window, recti(400, 36, 450, 62), 0, 0, 255, 5, 0);
@g = GuiSpinbox(window, recti(400, 66, 450, 92), 0, 0, 255, 5, 0);
@a = GuiSpinbox(window, recti(400, 96, 450, 122), 0, 0, 255, 5, 0);
@display = ColorBox(window, recti(400, 126, 450, 152));
color = startCol;
addDialog(this);
}
void set_color(Color col) {
syncBoxColor(col);
syncRegionColor(col);
}
void syncBoxColor(Color col) {
r.value = col.r;
g.value = col.g;
b.value = col.b;
a.value = col.a;
col.a = 255;
display.color = col;
}
void syncRegionColor(Color col) {
Colorf hsv = Colorf(col);
value.pos = 1.f - hsv.value;
picker.value = 1.f - value.pos;
picker.hue = hsv.hue;
picker.sat = hsv.saturation;
picker.alpha = hsv.a;
col.a = 255;
display.color = col;
}
Color get_color() const {
Color col;
col.r = min(uint(r.value), 255);
col.g = min(uint(g.value), 255);
col.b = min(uint(b.value), 255);
col.a = min(uint(a.value), 255);
return col;
}
//Event callbacks
bool onGuiEvent(const GuiEvent& event) {
if(Closed)
return false;
if(event.type == GUI_Clicked) {
if(event.caller is accept) {
if(callback !is null)
callback.colorChosen(color);
close();
return true;
}
}
else if(event.type == GUI_Changed) {
if(event.caller is picker) {
syncBoxColor(picker.picked);
return true;
}
else if(event.caller is value) {
picker.value = 1.f - value.pos;
syncBoxColor(picker.color);
return true;
}
else {
syncRegionColor(color);
return true;
}
}
return Dialog::onGuiEvent(event);
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) override {
if(Closed)
return false;
if(event.type == MET_Scrolled && source is picker)
return value.onMouseEvent(event, value);
return Dialog::onMouseEvent(event, source);;
}
};
@@ -0,0 +1,49 @@
#section disable menu
import dialogs.Dialog;
import elements.GuiFileChooser;
import util.design_export;
class DesignExportDialog : Dialog {
const Design@ dsg;
const DesignClass@ targetClass;
GuiFileChooser@ chooser;
DesignExportDialog(const Design@ Dsg, const DesignClass@ Cls, IGuiElement@ bind) {
super(bind);
addTitle(locale::EXPORT_DESIGN);
@dsg = Dsg;
@targetClass = Cls;
@window.callback = this;
@chooser = GuiFileChooser(window, Alignment(Left+12, Top+38, Right-12, Bottom-12), modProfile["designs"], "", CFM_Filename);
chooser.selectedFilename = dsg.name + ".design";
height = 500;
}
void focus() {
Dialog::focus();
chooser.filename.focus(true);
}
//Event callbacks
void clickConfirm() {
write_design(dsg, chooser.getSelectedPath(true), targetClass);
}
bool onGuiEvent(const GuiEvent& event) {
if(event.caller is chooser && event.type == GUI_Confirmed) {
clickConfirm();
close();
return true;
}
return Dialog::onGuiEvent(event);
}
};
DesignExportDialog@ exportDesign(const Design@ dsg, const DesignClass@ cls, IGuiElement@ bind = null) {
DesignExportDialog dlg(dsg, cls, bind);
addDialog(dlg);
return dlg;
}
@@ -0,0 +1,135 @@
#section disable menu
import dialogs.Dialog;
import elements.GuiFileChooser;
import elements.GuiButton;
import elements.GuiText;
import elements.GuiDropdown;
import dialogs.MessageDialog;
import util.design_export;
class DesignImportDialog : Dialog {
GuiFileChooser@ chooser;
GuiText@ label;
GuiDropdown@ classList;
GuiButton@ confirm;
DesignImportDialog(IGuiElement@ bind) {
super(bind);
addTitle(locale::IMPORT_DESIGNS);
@chooser = GuiFileChooser(window, Alignment(Left+12, Top+38, Right-12, Bottom-38),
modProfile["designs"], "", CFM_Multiple);
chooser.list.autoMultiple = true;
@label = GuiText(window, Alignment(Left+16, Bottom-34, Left+0.35f, Bottom-12), locale::IMPORT_INTO_CLASS);
@classList = GuiDropdown(window, Alignment(Left+0.35f+4, Bottom-34, Left+0.8f, Bottom-12));
classList.addItem(locale::SAVED_CLASS);
uint cnt = playerEmpire.designClassCount;
for(uint i = 0; i < cnt; ++i)
classList.addItem(playerEmpire.getDesignClass(i).name);
@confirm = GuiButton(window, Alignment(Left+0.8f+4, Bottom-34, Right-12, Bottom-12), locale::IMPORT);
height = 500;
}
//Event callbacks
bool showDesign(const Design@ dsg) {
return false;
}
void clickConfirm() {
const DesignClass@ inClass;
if(classList.selected > 0)
@inClass = playerEmpire.getDesignClass(classList.selected - 1);
string errors;
bool hasErrors = false;
const Design@ errDesign;
array<string> selected;
chooser.getSelectedFiles(selected, true);
for(uint i = 0, cnt = selected.length; i < cnt; ++i) {
DesignDescriptor desc;
string fname = getBasename(selected[i]);
if(!read_design(selected[i], desc)) {
errors += "\n"+fname+":\n - ";
errors += locale::ERROR_INVALID_FILE+"\n";
hasErrors = true;
continue;
}
//Report design renames
const Design@ prevDesign = playerEmpire.getDesign(desc.name);
if(prevDesign !is null && !prevDesign.obsolete) {
errors += "\n"+fname+":\n - ";
desc.name = uniqueDesignName(desc.name, playerEmpire);
errors += format(locale::ERROR_DUPLICATE_DESIGN, desc.name);
errors += "\n";
hasErrors = true;
@prevDesign = null;
}
//Report all design errors
@desc.hull = getBestHull(desc, getHullTypeTag(desc.hull));
const Design@ dsg = makeDesign(desc);
if(dsg.hasFatalErrors()) {
uint errCnt = dsg.errorCount;
errors += "\n"+fname+":\n";
for(uint j = 0; j < errCnt; ++j)
errors += " - "+dsg.errors[j].text+"\n";
hasErrors = true;
@errDesign = dsg;
continue;
}
if(desc.settings !is null)
dsg.setSettings(desc.settings);
const DesignClass@ cls = inClass;
if(cls is null && desc.className.length != 0)
@cls = playerEmpire.getDesignClass(desc.className);
if(cls is null)
@cls = playerEmpire.getDesignClass(locale::DOWNLOAD_DESIGN_CLASS, true);
if(prevDesign !is null)
playerEmpire.changeDesign(prevDesign, dsg, cls);
else
playerEmpire.addDesign(cls, dsg);
}
//Report errors
if(hasErrors) {
if(selected.length == 1 && errDesign !is null) {
if(showDesign(errDesign))
return;
}
message(locale::IMPORT_ERRORS + "\n" + errors, null, elem);
}
}
void confirmDialog() {
clickConfirm();
close();
}
bool onGuiEvent(const GuiEvent& event) {
if(event.caller is chooser && event.type == GUI_Confirmed) {
clickConfirm();
close();
return true;
}
if(event.caller is confirm && event.type == GUI_Clicked) {
clickConfirm();
close();
return true;
}
return Dialog::onGuiEvent(event);
}
};
DesignImportDialog@ importDesigns(IGuiElement@ bind = null) {
DesignImportDialog dlg(bind);
addDialog(dlg);
return dlg;
}
+166
View File
@@ -0,0 +1,166 @@
import elements.BaseGuiElement;
import elements.GuiDraggable;
import elements.GuiSkinElement;
import elements.GuiText;
import elements.GuiButton;
import dialogs.IDialog;
from gui import navigateInto;
from dialog import addDialog, closeDialog, closeDialogs;
const int DIALOG_BUTTON_HEIGHT = 32;
const int DIALOG_PADDING = 7;
const int DIALOG_BUTTON_ROW = DIALOG_BUTTON_HEIGHT + DIALOG_PADDING;
const Color DIALOG_TITLE_COLOR(0x00bffeff);
class Dialog : IDialog, IGuiCallback {
bool Closed;
IGuiElement@ elem;
IGuiElement@ lastFocus;
GuiSkinElement@ bg;
BaseGuiElement@ window;
GuiSkinElement@ titleBox;
GuiText@ titleText;
GuiButton@ closeBtn;
int height;
int width;
Dialog(IGuiElement@ bind, bool bindInside = false) {
Closed = false;
@elem = bind;
width = 500;
height = 38+DIALOG_PADDING;
@lastFocus = getGuiFocus();
@window = GuiDraggable(bindInside ? bind : null, recti());
@window.callback = this;
@bg = GuiSkinElement(window, Alignment_Fill(), SS_Dialog);
updatePosition();
}
void addTitle(const string& title, FontType font = FT_Bold, bool closeButton = true, const Color& color = Color(0x00bffeff)) {
if(titleBox is null)
@titleBox = GuiSkinElement(window, recti_area(vec2i(1, 1), vec2i(width-3, 26)), SS_WindowTitle);
else
titleBox.size = vec2i(width-3, 26);
if(titleText is null) {
@titleText = GuiText(window, recti_area(vec2i(8, 4), vec2i(width-16, 22)));
titleText.font = font;
height += 26;
}
titleBox.color = color;
titleText.text = title;
if(closeButton && closeBtn is null) {
vec2i size = window.skin.getSize(SS_GameTabClose, SF_Normal);
int off = (26 - size.y) / 2 + 1;
@closeBtn = GuiButton(window, Alignment(Right-off-size.x, Top+off, Width=size.x, Height=size.y));
closeBtn.style = SS_GameTabClose;
}
}
void set_titleColor(const Color& color) {
if(titleBox !is null)
titleBox.color = color;
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
return false;
}
bool onKeyEvent(const KeyboardEvent& event, IGuiElement@ source) {
switch(event.type) {
case KET_Key_Down:
if(event.key == KEY_ESC) {
return true;
}
else if(event.key == KEY_ENTER) {
return true;
}
break;
case KET_Key_Up:
if(event.key == KEY_ESC) {
cancelDialog();
return true;
}
else if(event.key == KEY_ENTER) {
confirmDialog();
return true;
}
break;
}
return false;
}
bool onGuiEvent(const GuiEvent& event) {
if(event.caller is closeBtn && event.type == GUI_Clicked) {
cancelDialog();
return true;
}
return false;
}
void set_position(const vec2i& pos) {
window.position = pos;
}
void updatePosition() {
recti pos;
if(elem is null)
pos = recti(vec2i(), screenSize);
else
pos = elem.absolutePosition;
pos = recti_centered(pos, vec2i(width, height));
if(titleBox !is null)
titleBox.size = vec2i(width-3, 26);
window.position = pos.topLeft;
window.size = pos.size;
window.updateAbsolutePosition();
}
void focus() {
window.bringToFront();
navigateInto(window);
}
void cancelDialog() {
close();
}
void confirmDialog() {
window.emitConfirmed();
}
void close() {
Closed = true;
window.remove();
setGuiFocus(lastFocus);
}
bool get_closed() {
return Closed;
}
IGuiElement@ get_bound() {
return elem;
}
};
void alignAcceptButtons(BaseGuiElement@ accept, BaseGuiElement@ cancel) {
if(isWindows) {
//Legacy OS support
@accept.alignment = Alignment(Left+0.2f+12, Bottom-0.0f-DIALOG_BUTTON_ROW, Left+0.5f-6, Bottom-0.0f-DIALOG_PADDING);
@cancel.alignment = Alignment(Right-0.5f+6, Bottom-0.0f-DIALOG_BUTTON_ROW, Right-0.2f-12, Bottom-0.0f-DIALOG_PADDING);
}
else {
@accept.alignment = Alignment(Right-0.3f, Bottom-0.0f-DIALOG_BUTTON_ROW, Right-0.0f-DIALOG_PADDING, Bottom-0.0f-DIALOG_PADDING);
@cancel.alignment = Alignment(Right-0.6f, Bottom-0.0f-DIALOG_BUTTON_ROW, Right-0.3f-DIALOG_PADDING, Bottom-0.0f-DIALOG_PADDING);
}
}
+10
View File
@@ -0,0 +1,10 @@
import elements.IGuiElement;
interface IDialog {
void set_position(const vec2i& pos);
void updatePosition();
void close();
void focus();
bool get_closed();
IGuiElement@ get_bound();
};
+292
View File
@@ -0,0 +1,292 @@
import dialogs.Dialog;
import elements.GuiButton;
import elements.GuiText;
import elements.GuiTextbox;
import elements.GuiCheckbox;
import elements.GuiDropdown;
import elements.GuiSpinbox;
const int INPUT_LINE_HEIGHT = 26;
interface IInputDialogCallback {
void inputCallback(InputDialog@ dialog, bool accepted);
void changeCallback(InputDialog@ dialog);
};
class InputDialogCallback : IInputDialogCallback {
void inputCallback(InputDialog@ dialog, bool accepted) {
}
void changeCallback(InputDialog@ dialog) {
}
};
class InputDialog : Dialog {
IInputDialogCallback@ callback;
BaseGuiElement@[] inputs;
GuiButton@ accept;
GuiButton@ cancel;
int curPos;
InputDialog(IInputDialogCallback@ CB, IGuiElement@ bind) {
@callback = CB;
curPos = DIALOG_PADDING;
super(bind);
Dialog::addTitle("", FT_Bold);
curPos += 26;
@accept = GuiButton(bg, recti());
accept.text = locale::ACCEPT;
accept.tabIndex = 100;
@accept.callback = this;
@cancel = GuiButton(bg, recti());
cancel.text = locale::CANCEL;
cancel.tabIndex = 101;
@cancel.callback = this;
alignAcceptButtons(accept, cancel);
}
uint get_inputCount() {
return inputs.length;
}
void focus() {
Dialog::focus();
focusInput();
}
//**Generic functions for adding lines
void addLine(BaseGuiElement& label, int h = INPUT_LINE_HEIGHT) {
@label.parent = window;
@label.alignment = Alignment(Left+12, Top+curPos, Right-12, Top+curPos + h);
curPos += h + 6;
height += h + 6;
}
int addInput(BaseGuiElement& input, int h = INPUT_LINE_HEIGHT) {
@input.parent = window;
@input.alignment = Alignment(Left+12, Top+curPos, Right-12, Top+curPos + h);
input.tabIndex = inputs.length();
curPos += h + 6;
height += h + 6;
inputs.insertLast(input);
return inputs.length() - 1;
}
int addInput(BaseGuiElement@ label, BaseGuiElement& input, int h = INPUT_LINE_HEIGHT) {
if(label !is null) {
@label.parent = window;
@label.alignment = Alignment(Left+12, Top+curPos, Left+0.3f-6, Top+curPos + h);
}
@input.parent = window;
@input.alignment = Alignment(Left+0.3f+6, Top+curPos, Right-12, Top+curPos + h);
input.tabIndex = inputs.length;
curPos += h + 6;
height += h + 6;
inputs.insertLast(input);
return inputs.length - 1;
}
BaseGuiElement@ getInput(int num) {
return inputs[num];
}
void focusInput(uint num = 0) {
if(num < inputs.length) {
setGuiFocus(inputs[num]);
}
else {
setGuiFocus(null);
}
}
//**Text label lines
void addTitle(const string& title, FontType font = FT_Bold, bool closeButton = true, const Color& color = DIALOG_TITLE_COLOR) {
titleText.text = title;
titleText.font = font;
titleBox.color = color;
}
int addLabel(const string& label, FontType font = FT_Normal, double align = 0.0, bool register = false) {
GuiText@ txt = GuiText(null, recti(), label);
@txt.parent = window;
@txt.alignment = Alignment(Left+12, Top+curPos, Right-12, Top+curPos + INPUT_LINE_HEIGHT);
txt.font = font;
txt.horizAlign = align;
txt.wordWrap = true;
int h = txt.getTextDimension().height;
h += (INPUT_LINE_HEIGHT - txt.skin.getFont(txt.font).getLineHeight());
if(h != INPUT_LINE_HEIGHT) {
@txt.alignment = Alignment(Left+12, Top+curPos, Right-12, Top+curPos + h);
}
curPos += h + 6;
height += h + 6;
if(register) {
inputs.insertLast(txt);
return inputs.length - 1;
}
else {
return -1;
}
}
void setLabel(int num, const string& label) {
GuiText@ txt = cast<GuiText>(getInput(num));
if(txt !is null)
txt.text = label;
}
//**Textbox input lines
int addTextInput(const string& label, const string& defaultText, int height = INPUT_LINE_HEIGHT) {
GuiText@ lbl = GuiText(null, recti(), label);
GuiTextbox@ box = GuiTextbox(null, recti(), defaultText);
@box.callback = this;
int i = addInput(lbl, box, height);
if(height > INPUT_LINE_HEIGHT) {
box.multiLine = true;
lbl.vertAlign = 0.0;
}
box.updateTextPosition();
return i;
}
string getTextInput(int num) {
GuiTextbox@ box = cast<GuiTextbox@>(getInput(num));
return box.text;
}
void focusTextInput(int num, bool selectAll = false) {
GuiTextbox@ box = cast<GuiTextbox@>(getInput(num));
box.focus(selectAll);
}
//**Spinbox input lines
int addSpinboxInput(const string& label, double defaultValue = 0.0, double step = 1.0,
double minValue = -INFINITY, double maxValue = INFINITY, int decimals = 0) {
GuiText@ lbl = GuiText(null, recti(), label);
GuiSpinbox@ box = GuiSpinbox(null, recti(), defaultValue);
@box.callback = this;
box.step = step;
box.decimals = decimals;
box.min = minValue;
box.max = maxValue;
box.value = defaultValue;
return addInput(lbl, box);
}
double getSpinboxInput(int num) {
GuiSpinbox@ box = cast<GuiSpinbox@>(getInput(num));
return box.value;
}
//**Checkbox input lines
int addToggle(const string& label, bool defaultValue) {
GuiCheckbox@ box = GuiCheckbox(null, recti(), label, defaultValue);
@box.callback = this;
return addInput(null, box);
}
bool getToggle(int num) {
GuiCheckbox@ box = cast<GuiCheckbox@>(getInput(num));
return box.checked;
}
//** Dropdown lines
int addSelection(const string& label) {
GuiText@ lbl = GuiText(null, recti(), label);
lbl.vertAlign = 0.0;
GuiDropdown@ box = GuiDropdown(null, recti());
return addInput(lbl, box);
}
void addItem(const string& value, bool select = false) {
addItem(inputs.length() - 1, value, select);
}
void addItem(int num, const string& value, bool select = false) {
GuiDropdown@ box = cast<GuiDropdown@>(getInput(num));
box.addItem(value);
if(select)
box.selected = box.itemCount - 1;
}
int getSelection(int num) {
GuiDropdown@ box = cast<GuiDropdown@>(getInput(num));
return box.selected;
}
string getSelectionValue(int num) {
GuiDropdown@ box = cast<GuiDropdown@>(getInput(num));
int sel = box.selected;
if(sel >= 0)
return box.getItem(sel);
else
return "";
}
//Close callback
void close() {
close(false);
}
void close(bool accepted) {
if(callback !is null)
callback.inputCallback(this, accepted);
Dialog::close();
}
void confirmDialog() {
close(true);
}
//Event callbacks
bool onGuiEvent(const GuiEvent& event) {
if(Closed)
return false;
if(event.type == GUI_Clicked && (event.caller is accept || event.caller is cancel)) {
close(event.caller is accept);
return true;
}
else if(event.type == GUI_Confirmed) {
int ind = -1;
for(uint i = 0, cnt = inputs.length(); i < cnt; ++i) {
if(inputs[i] is cast<BaseGuiElement@>(event.caller)) {
ind = i;
break;
}
}
if(ind < 0)
return false;
if(uint(ind) == inputs.length() - 1)
close(true);
else
setGuiFocus(inputs[ind + 1]);
return true;
}
else if(event.type == GUI_Changed) {
if(callback !is null)
callback.changeCallback(this);
}
return Dialog::onGuiEvent(event);
}
};
+45
View File
@@ -0,0 +1,45 @@
import dialogs.Dialog;
import elements.GuiFileChooser;
class LoadDialog : Dialog {
GuiFileChooser@ chooser;
GuiButton@ confirm;
LoadDialog(IGuiElement@ bind, const string& folder) {
super(bind);
addTitle(locale::LOAD);
@window.callback = this;
@chooser = GuiFileChooser(window, Alignment(Left+12, Top+38, Right-12, Bottom-12), folder, "", CFM_Single);
@confirm = GuiButton(window, Alignment(Left+0.8f+4, Bottom-42, Right-12, Bottom-12), locale::LOAD);
confirm.disabled = true;
height = 500;
addDialog(this);
}
void focus() {
Dialog::focus();
}
string get_path() {
return chooser.getSelectedPath(true);
}
//Event callbacks
void clickConfirm() {
}
bool onGuiEvent(const GuiEvent& event) {
if((event.caller is chooser && event.type == GUI_Confirmed)
|| (event.caller is confirm && event.type == GUI_Clicked)) {
if(chooser.selectedCount != 0)
clickConfirm();
close();
return true;
}
if(event.caller is chooser && event.type == GUI_Changed)
confirm.disabled = chooser.selectedCount == 0;
return Dialog::onGuiEvent(event);
}
};
+226
View File
@@ -0,0 +1,226 @@
import dialogs.Dialog;
enum MaterialChooserMode {
MCM_All,
MCM_Materials,
MCM_Spritesheets,
};
MaterialChooser@ openMaterialChooser(MaterialChoiceCallback@ callback, MaterialChooserMode mode = MCM_All) {
MaterialChooser@ matDialog = MaterialChooser(callback, null, mode);
addDialog(matDialog);
return matDialog;
}
interface MaterialChoiceCallback {
void onMaterialChosen(const Material@ material, const string& id);
void onSpriteSheetChosen(const SpriteSheet@ spritebank, uint spriteIndex, const string& id);
};
class MaterialChooser : Dialog {
MaterialList@ materials;
MaterialChoiceCallback@ matCallback;
MaterialChooser(MaterialChoiceCallback@ userCallback, IGuiElement@ bind, MaterialChooserMode mode) {
super(bind);
width = 1024;
height = 768;
@materials = MaterialList(window, recti(4,4,1020,764), mode);
@matCallback = userCallback;
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
return false;
}
bool onKeyEvent(const KeyboardEvent& event, IGuiElement@ source) {
return false;
}
bool onGuiEvent(const GuiEvent& event) {
if(event.type == GUI_Clicked && event.caller is materials) {
if(materials.material !is null)
matCallback.onMaterialChosen(materials.material, materials.identifier);
else if(materials.spriteSheet !is null)
matCallback.onSpriteSheetChosen(materials.spriteSheet, materials.spriteIndex, materials.identifier);
closeDialog(this);
return true;
}
return false;
}
};
class MaterialList : BaseGuiElement {
const Material@ material;
const SpriteSheet@ spriteSheet;
string identifier;
uint spriteIndex;
MaterialChooserMode mode;
int scroll;
int iconSize, margin;
uint rowIconCount;
vec2i hoverPos, clickPos;
MaterialList(IGuiElement@ parent, const recti& position, MaterialChooserMode Mode) {
super(parent, position);
hoverPos = vec2i(-1,-1);
iconSize = 96;
margin = 2;
mode = Mode;
rowIconCount = (AbsolutePosition.width - (2 * margin) / (iconSize + margin));
}
vec2i absoluteToMatPos(int x, int y) const {
return vec2i((x - (AbsolutePosition.topLeft.x + 4)) / (iconSize + margin),
(y - (AbsolutePosition.topLeft.y + 4) + scroll) / (iconSize + margin));
}
void draw() {
@material = null;
@spriteSheet = null;
vec2i at;
int x = margin, y = margin - scroll;
int height = AbsolutePosition.height;
int row = 0, col = 0;
//Draw each material
uint count = getMaterialCount();
if(mode == MCM_All || mode == MCM_Materials) {
for(uint i = 0; i < count; ++i) {
if(y + iconSize >= 0 && y - iconSize < height) {
const Material@ mat = getMaterial(i);
if(hoverPos.x == col && hoverPos.y == row) {
@material = mat;
identifier = getMaterialName(i);
at = vec2i(x-iconSize/2,y-iconSize/2);
}
else {
mat.draw(recti(x,y,x+iconSize,y+iconSize) + AbsolutePosition.topLeft, Color(0xffffffff), shader::BaseTexture);
}
}
x += iconSize + margin;
col += 1;
if(x+iconSize > AbsolutePosition.width - margin) {
x = margin;
y += iconSize + margin;
col = 0;
row += 1;
}
}
//Start on a new line when we switch to sprite sheets
if(col != 0) {
x = margin;
y += iconSize + margin;
col = 0;
row += 1;
}
}
//Draw each sprite in each sprite sheet
if(mode == MCM_All || mode == MCM_Spritesheets) {
count = getSpriteSheetCount();
for(uint i = 0; i < count; ++i) {
const SpriteSheet@ sheet = getSpriteSheet(i);
uint spriteCount = sheet.count;
for(uint j = 0; j < spriteCount; ++j) {
if(y + iconSize >= 0 && y - iconSize < height) {
if(hoverPos.x == col && hoverPos.y == row) {
at = vec2i(x-iconSize/2,y-iconSize/2);
@spriteSheet = sheet;
spriteIndex = j;
identifier = getSpriteSheetName(i);
}
else {
sheet.draw(j, recti_area(vec2i(x,y),vec2i(iconSize)) + AbsolutePosition.topLeft, shader::BaseTexture);
}
}
x += iconSize + margin;
col += 1;
if(x+iconSize > AbsolutePosition.width - margin) {
x = margin;
y += iconSize + margin;
col = 0;
row += 1;
}
}
}
}
if(material !is null || spriteSheet !is null) {
if(at.x < margin)
at.x = margin;
else if(at.x + (iconSize*2) > AbsolutePosition.width - margin)
at.x = AbsolutePosition.width - margin - (iconSize*2);
if(at.y < margin)
at.y = margin;
else if(at.y + (iconSize*2) > AbsolutePosition.height - margin)
at.y = AbsolutePosition.height - margin - (iconSize*2);
recti bgRect = recti(at.x - margin, at.y - margin, at.x + (iconSize*2) + margin, at.y + (iconSize*2) + margin) + AbsolutePosition.topLeft;
if(material !is null) {
drawRectangle(bgRect, Color(0x70a070ff));
vec2i size = material.size;
if(size.x > iconSize * 2 || size.y > iconSize * 2) {
double factor = max(double(size.x), double(size.y)) / double(iconSize * 2);
size /= factor;
}
material.draw(recti_area(at + vec2i(iconSize) - size * 0.5,size) + AbsolutePosition.topLeft, Color(0xffffffff), shader::BaseTexture);
skin.getFont(FT_Normal).draw(bgRect, identifier, Color(0x000000ff), vertAlign = 0.0);
}
else if(spriteSheet !is null) {
drawRectangle(bgRect, Color(0x70a070ff));
vec2i size = spriteSheet.size;
if(size.x > iconSize * 2 || size.y > iconSize * 2) {
double factor = max(double(size.x), double(size.y)) / double(iconSize * 2);
size /= factor;
}
spriteSheet.draw(spriteIndex, recti_area(at + vec2i(iconSize) - size * 0.5,size) + AbsolutePosition.topLeft, shader::BaseTexture);
skin.getFont(FT_Normal).draw(bgRect, identifier, Color(0x000000ff), vertAlign = 0.0);
}
}
BaseGuiElement::draw();
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
if(event.type == MET_Button_Up) {
vec2i pos = absoluteToMatPos(event.x, event.y);
if(pos == clickPos) {
emitClicked();
}
return true;
}
else if(event.type == MET_Button_Down) {
clickPos = absoluteToMatPos(event.x, event.y);
return true;
}
else if(event.type == MET_Moved) {
hoverPos = absoluteToMatPos(event.x, event.y);
}
else if(event.type == MET_Scrolled) {
scroll -= int(event.y * 32.0);
if(scroll < 0)
scroll = 0;
}
return BaseGuiElement::onMouseEvent(event, source);
}
};
+57
View File
@@ -0,0 +1,57 @@
import dialogs.Dialog;
import elements.GuiButton;
import elements.GuiMarkupText;
import elements.GuiTextbox;
interface MessageDialogCallback {
void messageCallback(MessageDialog@ dialog);
};
class MessageDialog : Dialog {
MessageDialogCallback@ callback;
GuiMarkupText@ txt;
GuiButton@ ok;
MessageDialog(const string& text, MessageDialogCallback@ CB, IGuiElement@ bind) {
@callback = CB;
super(bind);
addTitle(locale::NOTICE);
@ok = GuiButton(bg, recti());
ok.text = locale::OK;
if(isWindows) {
//Legacy OS support
@ok.alignment = Alignment(Left+0.4f+DIALOG_PADDING, Bottom-DIALOG_BUTTON_ROW, Right-0.4f-DIALOG_PADDING, Bottom-DIALOG_PADDING);
}
else {
@ok.alignment = Alignment(Right-0.2f-DIALOG_PADDING, Bottom-DIALOG_BUTTON_ROW, Right-0.0f-DIALOG_PADDING, Bottom-DIALOG_PADDING);
}
@txt = GuiMarkupText(window, recti(DIALOG_PADDING, DIALOG_PADDING+22, width - DIALOG_PADDING, 50), text);
height = txt.size.height+22;
height += 56;
}
//Close callback
void close() {
if(callback !is null)
callback.messageCallback(this);
Dialog::close();
}
//Event callbacks
bool onGuiEvent(const GuiEvent& event) {
if(event.type == GUI_Clicked && event.caller is ok && !Closed) {
close();
return true;
}
return Dialog::onGuiEvent(event);
}
};
MessageDialog@ message(const string& msg, MessageDialogCallback@ cb = null, IGuiElement@ bind = null) {
MessageDialog@ dlg = MessageDialog(msg, cb, bind);
addDialog(dlg);
return dlg;
}
+126
View File
@@ -0,0 +1,126 @@
import dialogs.Dialog;
import elements.GuiButton;
import elements.GuiMarkupText;
import elements.GuiTextbox;
import elements.GuiText;
interface QuestionDialogCallback {
void questionCallback(QuestionDialog@ dialog, int answer);
};
class QuestionDialog : Dialog {
QuestionDialogCallback@ callback;
GuiText@ txt;
GuiButton@[] buttons;
QuestionDialog(const string& title, const string& text, QuestionDialogCallback@ CB, IGuiElement@ bind) {
@callback = CB;
super(bind);
int y = 0;
if(title.length != 0) {
addTitle(title);
y += 26;
}
GuiMarkupText@ txt = GuiMarkupText(window, recti(DIALOG_PADDING, DIALOG_PADDING+y, width - DIALOG_PADDING, 50+y), text);
height = txt.size.height+y;
height += 50+DIALOG_PADDING;
}
string get_text() {
return txt.text;
}
//Button management
void addButton(const string& str, const Color& color = colors::White) {
if(isWindows) {
//Legacy OS support
GuiButton@ btn = GuiButton(window, recti(vec2i(), vec2i(130, DIALOG_BUTTON_HEIGHT)), str);
buttons.insertLast(btn);
btn.color = color;
int start = buttons.length() * (130 + DIALOG_PADDING) - DIALOG_PADDING;
start = (width - start) / 2;
for(uint i = 0, cnt = buttons.length(); i < cnt; ++i) {
buttons[i].position = vec2i(start, height - DIALOG_BUTTON_ROW);
start += 152;
}
}
else {
recti pos = recti_area(
vec2i(width - (buttons.length() + 1) * (130 + DIALOG_PADDING), height - DIALOG_BUTTON_ROW),
vec2i(130, DIALOG_BUTTON_HEIGHT));
GuiButton@ btn = GuiButton(window, pos, str);
btn.color = color;
buttons.insertLast(btn);
}
}
//Close callback
void close() {
close(-1);
}
void close(int answer) {
if(callback !is null)
callback.questionCallback(this, answer);
Dialog::close();
}
bool onGuiEvent(const GuiEvent& event) {
if(Closed)
return false;
if(event.type == GUI_Clicked) {
for(uint i = 0, cnt = buttons.length(); i < cnt; ++i) {
if(buttons[i] is cast<GuiButton@>(event.caller)) {
close(i);
return true;
}
}
}
else if(event.type == GUI_Confirmed) {
close(0);
}
return Dialog::onGuiEvent(event);
}
};
enum QuestionAnswer {
QA_Yes,
QA_No,
};
QuestionDialog@ question(const string& msg, QuestionDialogCallback@ cb = null, IGuiElement@ bind = null) {
QuestionDialog@ dlg = QuestionDialog(" ", msg, cb, bind);
dlg.addButton(locale::YES, colors::Green);
dlg.addButton(locale::NO, colors::Red);
addDialog(dlg);
return dlg;
}
QuestionDialog@ question(const string& msg, const string& yes, const string& no, QuestionDialogCallback@ cb = null, IGuiElement@ bind = null) {
QuestionDialog@ dlg = QuestionDialog(" ", msg, cb, bind);
dlg.addButton(yes, colors::Green);
dlg.addButton(no, colors::Red);
addDialog(dlg);
return dlg;
}
QuestionDialog@ question(const string& title, const string& msg, QuestionDialogCallback@ cb = null, IGuiElement@ bind = null) {
QuestionDialog@ dlg = QuestionDialog(title, msg, cb, bind);
dlg.addButton(locale::YES, colors::Green);
dlg.addButton(locale::NO, colors::Red);
addDialog(dlg);
return dlg;
}
QuestionDialog@ question(const string& title, const string& msg, const string& yes, const string& no, QuestionDialogCallback@ cb = null, IGuiElement@ bind = null) {
QuestionDialog@ dlg = QuestionDialog(title, msg, cb, bind);
dlg.addButton(yes, colors::Green);
dlg.addButton(no, colors::Red);
addDialog(dlg);
return dlg;
}
+43
View File
@@ -0,0 +1,43 @@
import dialogs.Dialog;
import elements.GuiFileChooser;
class SaveDialog : Dialog {
const Design@ dsg;
const DesignClass@ targetClass;
GuiFileChooser@ chooser;
SaveDialog(IGuiElement@ bind, const string& folder, const string& defaultName) {
super(bind);
addTitle(locale::EXPORT_DESIGN);
@window.callback = this;
@chooser = GuiFileChooser(window, Alignment(Left+12, Top+38, Right-12, Bottom-12), folder, "", CFM_Filename);
chooser.selectedFilename = defaultName;
height = 500;
addDialog(this);
}
void focus() {
Dialog::focus();
chooser.filename.focus(true);
}
string get_path() {
return chooser.getSelectedPath(true);
}
//Event callbacks
void clickConfirm() {
}
bool onGuiEvent(const GuiEvent& event) {
if(event.caller is chooser && event.type == GUI_Confirmed) {
if(chooser.selectedFilename.length != 0)
clickConfirm();
close();
return true;
}
return Dialog::onGuiEvent(event);
}
};
@@ -0,0 +1,33 @@
//Dialogs can be given a unique id on
//a per-module basis, so duplicate dialogs
//are not allowed.
Dialog@[] uniqueDialogs;
void addDialog(uint num, Dialog@ dlg) {
if(num >= uniqueDialogs.length)
uniqueDialogs.length = num + 1;
if(uniqueDialogs[num] !is null)
closeDialog(uniqueDialogs[num]);
@uniqueDialogs[num] = dlg;
addDialog(dlg);
}
bool focusDialog(uint num) {
if(uniqueDialogs.length <= num)
return false;
Dialog@ dlg = uniqueDialogs[num];
if(dlg is null || dlg.closed)
return false;
dlg.focus();
return true;
}
Dialog@ getUniqueDialog(uint num) {
if(uniqueDialogs.length <= num)
return null;
Dialog@ dlg = uniqueDialogs[num];
if(dlg is null || dlg.closed)
return null;
return dlg;
}