55. Configuration Files¶
Learn how to read and write configuration files such as CSV, INI, JSON, TOML, and XML.
55.1 Configuration Files Overview¶
- Siv3D supports reading and writing the following configuration file formats:
File Format | Read | Write |
---|---|---|
CSV | ✅ | ✅ |
INI | ✅ | ✅ |
JSON | ✅ | ✅ |
TOML | ✅ | |
XML | ✅ |
55.2 Reading CSV¶
- Use the
CSV
class to parse and read data from CSV files - Pass the path of the text file you want to read to the
CSV
constructor - The file path should be a relative path based on the folder where the executable is located (the App folder during development) or an absolute path
- You can check if the reading was successful with
if (csv)
orif (not csv)
- CSV data is read in the format of
Array<Array<String>>
, and you can get the text at rowrow
and columncol
using the subscript operator[row][col]
row
andcol
are counted from0
.rows()
returns the number of rows in the CSV data, and.columns(row)
returns the number of columns in rowrow
# include <Siv3D.hpp>
struct Item
{
// Item label
String label;
// Item top-left position
Point pos;
};
void Main()
{
// Load data from CSV file
const CSV csv{ U"example/csv/config.csv" };
if (not csv) // If loading failed
{
throw Error{ U"Failed to load `config.csv`" };
}
// For each row
for (size_t row = 0; row < csv.rows(); ++row)
{
// Display the contents of each column separated by tabs
String line;
for (size_t col = 0; col < csv.columns(row); ++col)
{
line += (csv[row][col] + U'\t');
}
Print << line;
}
Print << U"----";
// Get each element and apply to window and scene settings
{
const String title = csv[1][1];
const int32 width = Parse<int32>(csv[2][1]);
const int32 height = Parse<int32>(csv[3][1]);
const bool sizable = Parse<bool>(csv[4][1]);
const ColorF background = Parse<ColorF>(csv[5][1]);
Window::SetTitle(title);
Window::Resize(width, height);
Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
Scene::SetBackground(background);
}
{
const Array<int32> values = csv[6][1].split(U',').map(Parse<int32>);
Print << values;
}
// Create an array of items from CSV data
Array<Item> items;
{
const size_t itemCount = Parse<size_t>(csv[7][1]);
const size_t baseRow = 8;
for (size_t i = 0; i < itemCount; ++i)
{
items << Item
{
.label = csv[baseRow + i * 2][1],
.pos = Parse<Point>(csv[baseRow + i * 2 + 1][1]),
};
}
}
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
while (System::Update())
{
// Draw items
for (const auto& item : items)
{
const Rect rect{ item.pos, 180, 80 };
rect.draw();
font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
}
}
}
55.3 Writing CSV¶
- To write a CSV file, create an empty CSV object and add data from the first row using
.writeRow()
,.write()
,.newLine()
, etc. - Formattable values are automatically converted to strings
- Finally, save with
.save(path)
- If an element contains the character ",", that element is saved enclosed in quotation marks
# include <Siv3D.hpp>
void Main()
{
CSV csv;
// Write one row
csv.writeRow(U"item", U"price", U"count");
csv.writeRow(U"Sword", 500, 1);
// Write item by item
csv.write(U"Arrow");
csv.write(400);
csv.write(2);
csv.newLine();
csv.writeRow(U"Shield", 300, 3);
csv.writeRow(U"Carrot Seed", 20, 4);
csv.writeRow(U"aa, bb, cc", 10, 5);
csv.writeRow(Point{ 20, 30 }, Palette::Red, 100);
// Save
csv.save(U"tutorial.csv");
while (System::Update())
{
}
}
tutorial.csv
item,price,count
Sword,500,1
Arrow,400,2
Shield,300,3
Carrot Seed,20,4
"aa, bb, cc",10,5
"(20, 30)","(255, 0, 0, 255)",100
55.4 Updating CSV¶
- You can modify part of the loaded CSV data and then resave it to a file
# include <Siv3D.hpp>
void Main()
{
CSV csv{ U"example/csv/config.csv" };
if (not csv)
{
throw Error{ U"Failed to load `config.csv`" };
}
// Modify data
csv[2][1] = Format(1280);
csv[3][1] = Format(720);
// Add data
csv.writeRow(U"Hello.Siv3D", 12345);
csv.save(U"tutorial.csv");
while (System::Update())
{
}
}
tutorial.csv
Name,Value
Window.title,My application
Window.width,1280
Window.height,720
Window.sizable,false
Scene.background,"(0.8, 0.9, 1.0)"
Array.values,"11, 22, 33, 44, 55"
Items.count,3
Item.label,Forest
Item.pos,"(100, 100)"
Item.label,Ocean
Item.pos,"(300, 200)"
Item.label,Mountain
Item.pos,"(500, 100)"
Hello.Siv3D,12345
55.5 Reading INI¶
- Use the
INI
class to parse and read data from INI files - Pass the path of the text file you want to read to the
INI
constructor - The file path should be a relative path based on the folder where the executable is located (the App folder during development) or an absolute path
- You can check if the reading was successful with
if (ini)
orif (not ini)
- INI data is read in the format of
HashTable<String, String>
for each section, and you can get the text of name NAME in section SECTION using the subscript operator[U"SECTION.NAME"]
# include <Siv3D.hpp>
struct Item
{
// Item label
String label;
// Item top-left position
Point pos;
};
void Main()
{
// Load data from INI file
const INI ini{ U"example/ini/config.ini" };
if (not ini) // If loading failed
{
throw Error{ U"Failed to load `config.ini`" };
}
// List all sections
for (const auto& section : ini.sections())
{
// Section name
Print << U"[{}]"_fmt(section.section);
// List all records in the section
for (auto&& [key, value] : section.keys)
{
// Key and value
Print << U"{} = {}"_fmt(key, value);
}
}
Print << U"----";
// Get each element and apply to window and scene settings
{
const String title = ini[U"Window.title"];
const int32 width = Parse<int32>(ini[U"Window.width"]);
const int32 height = Parse<int32>(ini[U"Window.height"]);
const bool sizable = Parse<bool>(ini[U"Window.sizable"]);
const ColorF background = Parse<ColorF>(ini[U"Scene.background"]);
Window::SetTitle(title);
Window::Resize(width, height);
Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
Scene::SetBackground(background);
}
{
const Array<int32> values = ini[U"Array.values"].split(U',').map(Parse<int32>);
Print << values;
}
// Create an array of items from INI data
Array<Item> items;
{
const size_t itemCount = Parse<size_t>(ini[U"Items.count"]);
for (size_t i = 0; i < itemCount; ++i)
{
items << Item
{
.label = ini[U"Item{}.label"_fmt(i)],
.pos = Parse<Point>(ini[U"Item{}.pos"_fmt(i)]),
};
}
}
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
while (System::Update())
{
// Draw items
for (const auto& item : items)
{
const Rect rect{ item.pos, 180, 80 };
rect.draw();
font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
}
}
}
55.6 Writing INI¶
- To write an INI file, create an empty INI object and add sections and records using
.addSection(section name)
,.write(section, key, value)
, etc. - Finally, save with
.save(path)
# include <Siv3D.hpp>
void Main()
{
INI ini;
// Add sections
ini.addSection(U"Item");
ini.addSection(U"Setting");
ini.write(U"Item", U"Sword", 500);
ini.write(U"Item", U"Arrow", 400);
ini.write(U"Item", U"Shield", 300);
ini.write(U"Item", U"Carrot Seed", 20);
ini.write(U"Setting", U"pos", Point{ 20, 30 });
ini.write(U"Setting", U"color", Palette::Red);
// Save
ini.save(U"tutorial.ini");
while (System::Update())
{
}
}
tutorial.ini
[Item]
Sword = 500
Arrow = 400
Shield = 300
Carrot Seed = 20
[Setting]
pos = (20, 30)
color = (255, 0, 0, 255)
55.7 Updating INI¶
- You can modify part of the loaded INI data and then resave it to a file
# include <Siv3D.hpp>
void Main()
{
INI ini{ U"example/ini/config.ini" };
if (not ini)
{
throw Error{ U"Failed to load `config.ini`" };
}
// Modify data
ini[U"Window.width"] = 1280;
ini[U"Window.height"] = 720;
// Add data
ini.addSection(U"Siv3D");
ini.write(U"Siv3D", U"message", U"Hello!");
// Delete data
ini.removeSection(U"Item2");
ini.save(U"tutorial.ini");
while (System::Update())
{
}
}
tutorial.ini
[Window]
title = My application
width = 1280
height = 720
sizable = false
[Scene]
background = (0.8, 0.9, 1.0)
[Array]
values = 11, 22, 33, 44, 55
[Items]
count = 3
[Item0]
label = Forest
pos = (100, 100)
[Item1]
label = Ocean
pos = (300, 200)
[Siv3D]
message = Hello!
55.8 Reading JSON¶
- Use the
JSON
class to parse and read data from JSON files - Pass the path of the text file you want to read to
JSON::Load()
- The file path should be a relative path based on the folder where the executable is located (the App folder during development) or an absolute path
- You can check if the reading was successful with
if (json)
orif (not json)
- You can recursively traverse all elements of JSON data as shown in the
ShowObject()
function in the next sample - You can also directly get the desired value by specifying the path with the subscript operator
[U"NAME1"][U"NAME2]...
# include <Siv3D.hpp>
struct Item
{
// Item label
String label;
// Item top-left position
Point pos;
};
// Recursively display JSON elements
void ShowObject(const JSON& value)
{
switch (value.getType())
{
case JSONValueType::Empty:
Console << U"empty";
break;
case JSONValueType::Null:
Console << U"null";
break;
case JSONValueType::Object:
for (const auto& object : value)
{
Console << U"[{}]"_fmt(object.key);
ShowObject(object.value);
}
break;
case JSONValueType::Array:
for (auto&& [index, object] : value)
{
ShowObject(object);
}
break;
case JSONValueType::String:
Console << value.getString();
break;
case JSONValueType::Number:
Console << value.get<double>();
break;
case JSONValueType::Bool:
Console << value.get<bool>();
break;
}
}
void Main()
{
// Load data from JSON file
const JSON json = JSON::Load(U"example/json/config.json");
if (not json) // If loading failed
{
throw Error{ U"Failed to load `config.json`" };
}
// Display all JSON data
ShowObject(json);
Console << U"-----";
// Get each element and apply to window and scene settings
{
const String title = json[U"Window"][U"title"].getString();
const int32 width = json[U"Window"][U"width"].get<int32>();
const int32 height = json[U"Window"][U"height"].get<int32>();
const bool sizable = json[U"Window"][U"sizable"].get<bool>();
const ColorF background = json[U"Scene"][U"background"].get<ColorF>();
Window::SetTitle(title);
Window::Resize(width, height);
Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
Scene::SetBackground(background);
}
{
Array<int32> values;
for (auto&& [index, object] : json[U"Array"][U"values"])
{
values << object.get<int32>();
}
Console << values;
}
// Create an array of items from JSON data
Array<Item> items;
{
for (auto&& [index, object] : json[U"Items"])
{
items << Item
{
.label = object[U"label"].getString(),
.pos = Point{ object[U"pos"][U"x"].get<int32>(), object[U"pos"][U"y"].get<int32>() },
};
}
}
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
while (System::Update())
{
// Draw items
for (const auto& item : items)
{
const Rect rect{ item.pos, 180, 80 };
rect.draw();
font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
}
}
}
55.9 Writing JSON¶
- To write a JSON file, add data using the
operator[]
ofJSON
, and finally save with.save(path)
- Objects are recorded in dictionary order
- For arrays, you can add elements like
json[U"Array"].push_back(100);
# include <Siv3D.hpp>
void Main()
{
JSON json;
json[U"Item"][U"Sword"][U"price"] = 500;
json[U"Item"][U"Arrow"][U"price"] = 400;
json[U"Item"][U"Shield"][U"price"] = 300;
json[U"Item"][U"Carrot Seed"][U"price"] = 20;
json[U"Setting"][U"pos"] = Point{ 20, 30 };
json[U"Setting"][U"color"] = Palette::Red;
json[U"Array"].push_back(10);
json[U"Array"].push_back(20);
json[U"Array"].push_back(30);
json.save(U"tutorial.json");
while (System::Update())
{
}
}
tutorial.json
{
"Array": [
10,
20,
30
],
"Item": {
"Arrow": {
"price": 400
},
"Carrot Seed": {
"price": 20
},
"Shield": {
"price": 300
},
"Sword": {
"price": 500
}
},
"Setting": {
"color": "(255, 0, 0, 255)",
"pos": "(20, 30)"
}
}
55.10 Updating JSON¶
- You can modify part of the loaded JSON data and then resave it to a file
# include <Siv3D.hpp>
void Main()
{
JSON json = JSON::Load(U"example/json/config.json");
if (not json)
{
throw Error{ U"Failed to load `config.json`" };
}
// Modify data
json[U"Window"][U"width"] = 1280;
json[U"Window"][U"height"] = 720;
// Add data
json[U"Siv3D"][U"message"] = U"Hello!";
// Delete data
json[U"Items"].erase(2);
json.erase(U"Array");
json.save(U"tutorial.json");
while (System::Update())
{
}
}
tutorial.json
{
"Items": [
{
"label": "Forest",
"pos": {
"x": 100,
"y": 100
}
},
{
"label": "Ocean",
"pos": {
"x": 300,
"y": 200
}
}
],
"Scene": {
"background": "(0.8, 0.9, 1.0)"
},
"Siv3D": {
"message": "Hello!"
},
"Window": {
"height": 720,
"sizable": false,
"title": "My application",
"width": 1280
}
}
55.11 Stringifying JSON¶
.format()
ofJSON
converts JSON data to a formattedString
.formatMinimum()
returns a minimalString
that omits whitespace and line breaks for formatting
# include <Siv3D.hpp>
void Main()
{
JSON json = JSON::Load(U"example/json/config.json");
const String s = json.formatMinimum();
Print << s;
while (System::Update())
{
}
}
Output
{"Array":{"values":[11,22,33,44,55]},"Items":[{"label":"Forest","pos":{"x":100,"y":100}},{"label":"Ocean","pos":{"x":300,"y":200}},{"label":"Mountain","pos":{"x":500,"y":100}}],"Scene":{"background":"(0.8, 0.9, 1.0)"},"Window":{"height":600,"sizable":false,"title":"My application","width":800}}
55.12 JSON Literals¶
- You can directly write JSON data by adding
_json
to string literals - It is recommended to use raw string literals
UR
so that double quotes can be written directly - To convert from a regular
String
toJSON
, useJSON::Parse(s)
MakeJSON1()
,MakeJSON2()
, andMakeJSON3()
in the following code create the same JSON data
# include <Siv3D.hpp>
JSON MakeJSON1()
{
JSON json;
json[U"name"] = U"Albert";
json[U"age"] = 42;
json[U"object"] = JSON::Parse(U"{}");
return json;
}
JSON MakeJSON2()
{
return UR"({
"name": "Albert",
"age": 42,
"object": {}
})"_json;
}
JSON MakeJSON3()
{
const String s = UR"({
"name": "Albert",
"age": 42,
"object": {}
})";
return JSON::Parse(s);
}
void Main()
{
const JSON json1 = MakeJSON1();
const JSON json2 = MakeJSON2();
const JSON json3 = MakeJSON3();
Print << (json1 == json2);
Print << (json2 == json3);
while (System::Update())
{
}
}
55.13 JSON Validation¶
JSONValidator
verifies whether JSON data has appropriate structure and values based on JSON Schema- JSON Schema is JSON data that defines the structure, data types, value ranges, etc. of JSON data
- You can directly write JSON Schema by adding
jsonValidator
to string literals - In the following sample code, it shows that
json1
andjson3
do not conform to the specified Schema and the reasons
# include <Siv3D.hpp>
void Main()
{
const JSON json1 = UR"({
"name": "Albert",
"age": 42,
"object": {}
})"_json;
const JSON json2 = UR"({
"name": "Albert",
"age": 42,
"object": {
"string": "aaaa"
}
})"_json;
const JSON json3 = UR"({
"name": "Albert",
"age": 999,
"object": {
"string": "bbbb"
}
})"_json;
const JSONValidator validator = UR"({
"title": "A person",
"properties": {
"name": {
"description": "Name",
"type": "string"
},
"age": {
"description": "Age of the person",
"type": "number",
"minimum": 2,
"maximum": 200
},
"object": {
"type": "object",
"properties": {
"string": {
"type": "string"
}
},
"required": [
"string"
]
}
},
"required": [
"name",
"age",
"object"
],
"type": "object"
})"_jsonValidator;
Print << U"json1:";
{
JSONValidator::ValidationError error;
if (validator.validate(json1, error))
{
Print << U"OK";
}
else
{
Print << error;
}
}
Print << U"json2:";
{
JSONValidator::ValidationError error;
if (validator.validate(json2, error))
{
Print << U"OK";
}
else
{
Print << error;
}
}
Print << U"json3:";
{
JSONValidator::ValidationError error;
if (validator.validate(json3, error))
{
Print << U"OK";
}
else
{
Print << error;
}
}
while (System::Update())
{
}
}
Output
json1:
[JSONValidator::ValidationError] required property 'string' not found in object
json2:
OK
json3:
[JSONValidator::ValidationError] instance exceeds maximum of 200.000000
55.14 Reading TOML¶
- Use the
TOMLReader
class to parse and read data from TOML files - Pass the path of the text file you want to read to the
TOMLReader
constructor - The file path should be a relative path based on the folder where the executable is located (the App folder during development) or an absolute path
- You can check if the reading was successful with
if (toml)
orif (not toml)
- You can recursively traverse all elements of TOML data as shown in the
ShowTable()
function in the next sample - You can also directly get the desired value by specifying the path with the subscript operator
[U"NAME1.NAME2.NAME3..."]
Japanese is not supported
- Siv3D v0.6's
TOMLReader
cannot read TOML files containing non-ASCII characters - Support is planned for future versions
# include <Siv3D.hpp>
struct Item
{
// Item label
String label;
// Item top-left position
Point pos;
};
// Recursively display TOML elements
void ShowTable(const TOMLValue& value)
{
for (const auto& table : value.tableView())
{
switch (table.value.getType())
{
case TOMLValueType::Empty:
Console << U"[Empty] " << table.name;
break;
case TOMLValueType::Table:
Console << U"[Table] " << table.name;
ShowTable(table.value);
break;
case TOMLValueType::Array:
Console << U"[Array] " << table.name;
for (const auto& element : table.value.arrayView())
{
switch (element.getType())
{
case TOMLValueType::String:
Console << element.getString();
break;
case TOMLValueType::Number:
Console << element.get<double>();
break;
case TOMLValueType::Bool:
Console << element.get<bool>();
break;
case TOMLValueType::Date:
Console << element.getDate();
break;
case TOMLValueType::DateTime:
Console << element.getDateTime();
break;
default:
break;
}
}
break;
case TOMLValueType::TableArray:
Console << U"[TableArray] " << table.name;
for (const auto& table2 : table.value.tableArrayView())
{
ShowTable(table2);
}
break;
case TOMLValueType::String:
Console << U"[String] " << table.name;
Console << table.value.getString();
break;
case TOMLValueType::Number:
Console << U"[Number] " << table.name;
Console << table.value.get<double>();
break;
case TOMLValueType::Bool:
Console << U"[Bool] " << table.name;
Console << table.value.get<bool>();
break;
case TOMLValueType::Date:
Console << U"[Date] " << table.name;
Console << table.value.getDate();
break;
case TOMLValueType::DateTime:
Console << U"[DateTime] " << table.name;
Console << table.value.getDateTime();
break;
case TOMLValueType::Unknown:
Console << U"[Unknown] " << table.name;
break;
}
}
}
void Main()
{
// Load data from TOML file
const TOMLReader toml{ U"example/toml/config.toml" };
if (not toml) // If loading failed
{
throw Error{ U"Failed to load `config.toml`" };
}
// Display all TOML data
ShowTable(toml);
Console << U"-----";
// Get each element and apply to window and scene settings
{
const String title = toml[U"Window.title"].getString();
const int32 width = toml[U"Window.width"].get<int32>();
const int32 height = toml[U"Window.height"].get<int32>();
const bool sizable = toml[U"Window.sizable"].get<bool>();
const ColorF background = toml[U"Scene.background"].get<ColorF>();
Window::SetTitle(title);
Window::Resize(width, height);
Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
Scene::SetBackground(background);
}
{
Array<int32> values;
for (const auto& object : toml[U"Array.values"].arrayView())
{
values << object.get<int32>();
}
Console << values;
}
// Create an array of items from TOML data
Array<Item> items;
{
for (const auto& object : toml[U"Items"].tableArrayView())
{
items << Item
{
.label = object[U"label"].getString(),
.pos = Point{ object[U"pos.x"].get<int32>(), object[U"pos.y"].get<int32>() },
};
}
}
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
while (System::Update())
{
// Draw items
for (const auto& item : items)
{
const Rect rect{ item.pos, 180, 80 };
rect.draw();
font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
}
}
}
55.15 Reading XML¶
- Use the
XMLReader
class to parse and read data from XML files - Pass the path of the text file you want to read to the
XMLReader
constructor - The file path should be a relative path based on the folder where the executable is located (the App folder during development) or an absolute path
- You can check if the reading was successful with
if (xml)
orif (not xml)
- You can recursively traverse all elements of XML data as shown in the
ShowElements()
function in the next sample
# include <Siv3D.hpp>
struct Item
{
// Item label
String label;
// Item top-left position
Point pos;
};
// Recursively display XML elements
void ShowElements(const XMLElement& element)
{
for (auto e = element.firstChild(); e; e = e.nextSibling())
{
Console << U"<{}>"_fmt(e.name());
if (const auto attributes = e.attributes())
{
Console << attributes;
}
if (const auto text = e.text())
{
Console << text;
}
ShowElements(e);
Console << U"</{}>"_fmt(e.name());
}
}
void Main()
{
// Load data from XML file
const XMLReader xml(U"example/xml/config.xml");
if (not xml) // If loading failed
{
throw Error{ U"Failed to load `config.xml`" };
}
// Display all XML data
ShowElements(xml);
Console << U"-----";
Array<Item> items;
{
String title;
int32 width = Window::DefaultClientSize.x;
int32 height = Window::DefaultClientSize.y;
bool sizable = false;
ColorF background{ 0.0 };
Array<int32> values;
// Traverse elements to get desired values
for (auto elem = xml.firstChild(); elem; elem = elem.nextSibling())
{
const String name = elem.name();
if (name == U"Window")
{
for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
{
const String name2 = elem2.name();
if (name2 == U"title")
{
title = elem2.text();
}
else if (name2 == U"width")
{
width = Parse<int32>(elem2.text());
}
else if (name2 == U"height")
{
height = Parse<int32>(elem2.text());
}
else if (name2 == U"sizable")
{
sizable = Parse<bool>(elem2.text());
}
}
}
else if (name == U"Scene")
{
for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
{
const String name2 = elem2.name();
if (name2 == U"background")
{
background = Parse<ColorF>(elem2.text());
}
}
}
if (name == U"Array")
{
for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
{
values << Parse<int32>(elem2.text());
}
}
if (name == U"Items")
{
Item item;
for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
{
const String name2 = elem2.name();
if (name2 == U"label")
{
item.label = elem2.text();
}
else if (name2 == U"pos")
{
Point pos{ 0, 0 };
for (auto elem3 = elem2.firstChild(); elem3; elem3 = elem3.nextSibling())
{
const String name3 = elem3.name();
if (name3 == U"x")
{
pos.x = Parse<int32>(elem3.text());
}
else if (name3 == U"y")
{
pos.y = Parse<int32>(elem3.text());
}
}
item.pos = pos;
}
}
items << item;
}
}
Window::SetTitle(title);
Window::Resize(width, height);
Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
Scene::SetBackground(background);
Console << values;
}
const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
while (System::Update())
{
// Draw items
for (const auto& item : items)
{
const Rect rect{ item.pos, 180, 80 };
rect.draw();
font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
}
}
}