Open2

Siv3D v0.6.9 新規追加・更新サンプル

Ryo SuzukiRyo Suzuki

SimpleTable

様々なスタイルの表を描画できます。

# include <Siv3D.hpp> // OpenSiv3D v0.6.9

void Main()
{
	Window::Resize(1280, 720);
	Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
	const Font font1{ FontMethod::MSDF, 36 };
	const Font font2{ FontMethod::MSDF, 36, Typeface::Heavy };
	const Font font3{ FontMethod::MSDF, 36, Typeface::Bold };

	//
	// table 1
	//
	SimpleTable table1{ { 100, 200, 200 }, {
			.variableWidth = true,
			.font = font1,
			.columnHeaderFont = font3,
			.rowHeaderFont = font3,
		} };
	table1.push_back_row({ U"", U"日本", U"アメリカ" }, { 0, 0, 0 });
	table1.push_back_row({ U"面積", U"約 37 万 8 千平方キロメートル", U"約 983 万平方キロメートル" }, { 0, -1, -1 });
	table1.push_back_row({ U"人口", U"約 1 億 2 千万人", U"約 3 億 3 千万人" });
	table1.push_back_row({ U"言語", U"日本語", U"英語" });
	table1.push_back_row({ U"通貨", U"円 (JPY)", U"ドル (USD)" });

	//
	// table 2
	//
	SimpleTable table2{ { 160, 100, 100 }, {
			.cellHeight = 40,
			.borderThickness = 2,
			.backgroundColor = none,
			.textColor = ColorF{ 1.0 },
			.borderColor = ColorF{ 0.29, 0.33, 0.41 },
			.hasVerticalBorder = false,
			.hasOuterBorder = false,
			.font = font2,
			.fontSize = 24,
			.hoveredRow = [](const Point& index) -> Optional<ColorF>
			{
				if (index.y != 0)
				{
					return ColorF{ 1.0, 0.2 };
				}

				return none;
			},
		} };
	table2.push_back_row({ U"Player", U"Rank", U"Rate" }, { -1, 1, 1 });
	table2.push_back_row({ U"Alice", U"2", U"2832" });
	table2.push_back_row({ U"Bob", U"6", U"2540" });
	table2.push_back_row({ U"Carol", U"16", U"2315" });
	table2.push_back_row({ U"Eve", U"121", U"1874" });

	for (int32 y = 1; y < 3; ++y)
	{
		table2.setRowTextColor(y, ColorF{ 1.00, 0.7, 0.25 });
	}

	for (int32 y = 3; y < 5; ++y)
	{
		table2.setRowTextColor(y, ColorF{ 0.82, 0.56, 0.84 });
	}

	//
	// table 3
	//
	SimpleTable table3{ Array<double>(7, 60.0), {
			.borderThickness = 2,
			.backgroundColor = none,
			.borderColor = ColorF{ 1.0 },
			.hasOuterBorder = false,
			.font = font3,
			.hoveredCell = [](const Point& index) -> Optional<ColorF>
			{
				if (index.y != 0)
				{
					return ColorF{ 0.94 };
				}

				return none;
			},
		} };
	table3.push_back_row({ U"Sun", U"Mon", U"Tue", U"Wed", U"Thu", U"Fri", U"Sat" }, Array<int32>(7, 0));
	table3.setRowBackgroundColor(0, ColorF{ 1.00, 0.8, 0.7 });
	table3.push_back_row(Array<int32>(7, 1));
	table3.push_back_row();
	table3.push_back_row();
	table3.push_back_row();
	table3.push_back_row();
	table3.push_back_row();

	for (int32 i = 0; i < (7 * 6); ++i)
	{
		const auto date = Date{ 2023, 3, 26 } + Days{ i };
		const Point index{ (i % 7), (1 + (i / 7)) };
		table3.setText(index, Format(date.day));

		if (date.month != 4)
		{
			table3.setTextColor(index, ColorF{ 0.7 });
		}
	}

	//
	// table 4
	//
	SimpleTable table4{ { 100, 80, 80 }, {
			.cellHeight = 28.0,
			.hasHorizontalBorder = false,
			.font = font1,
			.fontSize = 16,
			.columnHeaderFont = font3,
			.columnHeaderFontSize = 14,
			.hoveredRow = [](const Point& index) -> Optional<ColorF>
			{
				if (index.y != 0)
				{
					return ColorF{ 1.0, 0.95, 0.90 };
				}

				return none;
			},
		} };
	table4.push_back_row({ U"", U"こだま701", U"のぞみ5" }, { -1, 0, 0 });
	table4.push_back_row({ U"東 京 発", U"6:30", U"6:33" });
	table4.push_back_row({ U"品 川 〃", U"6:37", U"6:40" });
	table4.push_back_row({ U"新横浜 〃", U"6:48", U"6:51" });
	table4.push_back_row({ U"小田原 〃", U"7:05", U"レ" });
	table4.push_back_row({ U"熱 海 〃", U"7:14", U"レ" });
	table4.push_back_row({ U"三 島 〃", U"7:26", U"レ" });
	table4.push_back_row({ U"新富士 〃", U"7:37", U"レ" });
	table4.push_back_row({ U"静 岡 〃", U"7:51", U"レ" });
	table4.push_back_row({ U"掛 川 〃", U"8:08", U"レ" });
	table4.push_back_row({ U"浜 松 〃", U"8:23", U"レ" });
	table4.push_back_row({ U"豊 橋 〃", U"8:39", U"レ" });
	table4.push_back_row({ U"三河安城〃", U"8:56", U"レ" });
	table4.push_back_row({ U"名古屋 着", U"9:06", U"8:10" });
	table4.push_back_row({ U"名古屋 発", U"", U"8:12" });

	//
	// table 5
	//
	SimpleTable table5{ { 80, 80, 80, 80 }, {
		.cellHeight = 26.0,
		.borderThickness = 2.0,
		.borderColor = ColorF{ 0.6 },
		.columnHeaderFont = font3,
		.columnHeaderFontSize = 15.0,
		.rowHeaderFont = font3,
		.rowHeaderFontSize = 15.0,
	} };
	table5.push_back_row({ U"", U"今日", U"明日", U"明後日" }, { 0, 0, 0, 0 });
	table5.push_back_row({ U"札幌", U"\U000F0597\U000F19B0\U000F0590", U"\U000F0590/\U000F0597", U"\U000F0590" });
	table5.push_back_row({ U"東京", U"\U000F0599\U000F19B0\U000F0590", U"\U000F0597/\U000F0590", U"\U000F0590\U000F19B0\U000F0597" });
	table5.push_back_row({ U"大阪", U"\U000F0590\U000F19B0\U000F0597", U"\U000F0597", U"\U000F0599/\U000F0590" });
	table5.push_back_row({ U"福岡", U"\U000F0597\U000F19B0\U000F0590", U"\U000F0590\U000F19B0\U000F0599", U"\U000F0599/\U000F0590" });
	table5.push_back_row({ U"沖縄", U"\U000F0590", U"\U000F0590/\U000F0597", U"\U000F0590\U000F19B0\U000F0599" });
	for (size_t y = 1; y < table5.rows(); ++y)
	{
		for (size_t x = 1; x < table5.columns(); ++x)
		{
			const bool isRainy = table5.getItem(y, x).text.includes(U'\U000F0597');
			const bool isSunny = table5.getItem(y, x).text.includes(U'\U000F0599');
			const bool isCloudy = table5.getItem(y, x).text.includes(U'\U000F0590');

			if (isRainy)
			{
				table5.setBackgroundColor(y, x, ColorF{ 0.7, 0.9, 1.0 });
			}
			else if (isSunny)
			{
				table5.setBackgroundColor(y, x, ColorF{ 1.0, 0.9, 0.7 });
			}
			else if (isCloudy)
			{
				table5.setBackgroundColor(y, x, ColorF{ 0.9 });
			}
		}
	}


	Optional<Point> table1ActiveIndex;

	while (System::Update())
	{
		// table1
		{
			if (SimpleGUI::Button(U"行を追加", Vec2{ 740, 40 }, 130))
			{
				table1.push_back_row({ 0, -1, -1 });
			}

			if (SimpleGUI::Button(U"行を削除", Vec2{ 740, 80 }, 130))
			{
				table1.pop_back_row();
			}

			constexpr Vec2 TablePos{ 40,40 };

			if (MouseL.down())
			{
				table1ActiveIndex = table1.cellIndex(TablePos, Cursor::Pos());
			}

			table1.draw(TablePos);

			if (table1ActiveIndex)
			{
				table1.cellRegion(TablePos, *table1ActiveIndex).drawFrame(2, 1, ColorF{ 0.11 });
			}
		}

		// table2
		{
			constexpr Vec2 TablePos{ 64, 372 };
			table2.region(TablePos).stretched(24, 12).rounded(10.0).draw(ColorF{ 0.18, 0.20, 0.35 });
			table2.draw(TablePos);
		}

		// table 3
		{
			constexpr Vec2 TablePos{ 500, 380 };
			table3.region(TablePos).stretched(10, 20).rounded(5).draw();
			table3.draw(TablePos);
		}

		// table 4
		{
			constexpr Vec2 TablePos{ 980, 220 };
			table4.draw(TablePos);
		}

		// table 5
		{
			constexpr Vec2 TablePos{ 914, 40 };
			table5.draw(TablePos);
		}
	}
}

SimpleTable でスプレッドシートを作るサンプル

https://twitter.com/Reputeless/status/1647506362300268545

# include <Siv3D.hpp>

void Main()
{
	Window::Resize(1280, 720);
	Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });

	constexpr int32 CellCountX = 8;
	constexpr int32 CellCountY = 15;

	Array<double> minColumnWidths((CellCountX + 1), 120);
	minColumnWidths[0] = 20;

	Array<String> columnNames = { U"" };
	for (int32 i = 1; i < (CellCountX + 1); ++i)
	{
		columnNames.push_back(String{ (U'A' + i - 1) });
	}

	SimpleTable table{ minColumnWidths, {
		.cellHeight = 36,
		.variableWidth = true,
	} };
	table.push_back_row(columnNames, Array<int32>((CellCountX + 1), 0));
	table.setRowBackgroundColor(0, ColorF{ 0.9 });

	for (int32 i = 1; i < (CellCountY + 1); ++i)
	{
		Array<String> row(CellCountX + 1);
		row[0] = U"{}"_fmt(i);

		Array<int32> rowAlignments((CellCountX + 1), 1);
		rowAlignments[0] = 0;

		table.push_back_row(row, rowAlignments);
		table.setBackgroundColor(i, 0, ColorF{ 0.9 });
	}

	Optional<Point> activeIndex;
	Optional<Point> nextActiveIndex;
	TextEditState textEditState;

	while (System::Update())
	{
		if (nextActiveIndex)
		{
			activeIndex = *nextActiveIndex;
			textEditState = TextEditState{ table.getItem(*activeIndex).text };
			textEditState.cursorPos = textEditState.text.length();
			textEditState.active = true;
			nextActiveIndex.reset();
		}

		{
			constexpr Vec2 TablePos{ 40, 40 };

			if (MouseL.down())
			{
				const auto newActiveIndex = table.cellIndex(TablePos, Cursor::Pos());

				if (newActiveIndex != activeIndex)
				{
					activeIndex = table.cellIndex(TablePos, Cursor::Pos());

					if (activeIndex)
					{
						textEditState = TextEditState{ table.getItem(*activeIndex).text };
						textEditState.cursorPos = textEditState.text.length();
						textEditState.active = true;
						MouseL.clearInput();
					}
				}
			}

			table.draw(TablePos);

			if (activeIndex && ((activeIndex->x != 0) && (activeIndex->y != 0)))
			{
				const RectF cellRegion = table.cellRegion(TablePos, *activeIndex);

				if (SimpleGUI::TextBox(textEditState, cellRegion.pos, cellRegion.w))
				{
					table.setText(*activeIndex, textEditState.text);
				}

				if (textEditState.enterKey)
				{
					nextActiveIndex = Point{ activeIndex->x, (activeIndex->y + 1) };
				}

				if ((1 < activeIndex->y) && KeyUp.down())
				{
					nextActiveIndex = Point{ activeIndex->x, (activeIndex->y - 1) };
				}

				if ((activeIndex->y < CellCountY) && KeyDown.down())
				{
					nextActiveIndex = Point{ activeIndex->x, (activeIndex->y + 1) };
				}

				if ((1 < activeIndex->x) && KeyLeft.down())
				{
					nextActiveIndex = Point{ (activeIndex->x - 1), activeIndex->y };
				}

				if ((activeIndex->x < CellCountX) && KeyRight.down())
				{
					nextActiveIndex = Point{ (activeIndex->x + 1), activeIndex->y };
				}

				if (KeyDelete.down())
				{
					textEditState.clear();
					table.setText(*activeIndex, U"");
				}
			}
		}
	}
}
Ryo SuzukiRyo Suzuki

SimpleGUI::TextArea()

複数行に対応したテキストボックスです。

# include <Siv3D.hpp> // OpenSiv3D v0.6.9

void Main()
{
	Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });

	TextAreaEditState textAreaEditState;

	bool enabled = true;

	while (System::Update())
	{
		if (SimpleGUI::Button(U"Clear", Vec2{ 40, 40 }, 100, TextInput::GetEditingText().isEmpty()))
		{
			textAreaEditState.clear();
		}

		SimpleGUI::CheckBox(enabled, U"enabled", Vec2{ 160, 40 });

		SimpleGUI::TextArea(textAreaEditState, Vec2{ 40, 90 }, SizeF{ 720, 300 }, SimpleGUI::PreferredTextAreaMaxChars, enabled);

		// 内容
		//ClearPrint();
		//Print << textAreaEditState.text;
	}
}