From 56da4e81d8dae199bffd654d4e7ea98ce8b24ae8 Mon Sep 17 00:00:00 2001
From: Nicolas Allemand <[EMAIL REDACTED]>
Date: Wed, 4 Dec 2024 12:12:43 +0100
Subject: [PATCH] examples: added homepage + categories pages + added CSS
(similar to wiki)
---
build-scripts/build-web-examples.pl | 105 +++++++++-
examples/template-category.html | 30 +++
examples/template-homepage.html | 32 ++++
examples/template.css | 284 ++++++++++++++++++++++++++++
examples/template.html | 114 ++++++++---
5 files changed, 532 insertions(+), 33 deletions(-)
create mode 100644 examples/template-category.html
create mode 100644 examples/template-homepage.html
create mode 100644 examples/template.css
diff --git a/build-scripts/build-web-examples.pl b/build-scripts/build-web-examples.pl
index 0053295e9f60b..6df912aa8c7c3 100755
--- a/build-scripts/build-web-examples.pl
+++ b/build-scripts/build-web-examples.pl
@@ -71,6 +71,38 @@ sub build_latest {
}
}
+sub get_categories {
+ my @categories = ();
+
+ opendir(my $dh, $examples_dir) or die("Couldn't opendir '$examples_dir': $!\n");
+ foreach my $dir (sort readdir $dh) {
+ next if ($dir eq '.') || ($dir eq '..'); # obviously skip current and parent entries.
+ next if not -d "$examples_dir/$dir"; # only care about subdirectories.
+
+ push @categories, $dir;
+ }
+ closedir($dh);
+
+ return @categories;
+}
+
+sub get_examples_for_category {
+ my $category = shift;
+
+ my @examples = ();
+
+ opendir(my $dh, "$examples_dir/$category") or die("Couldn't opendir '$examples_dir/$category': $!\n");
+ foreach my $dir (sort readdir $dh) {
+ next if ($dir eq '.') || ($dir eq '..'); # obviously skip current and parent entries.
+ next if not -d "$examples_dir/$category/$dir"; # only care about subdirectories.
+
+ push @examples, $dir;
+ }
+ closedir($dh);
+
+ return @examples;
+}
+
sub handle_example_dir {
my $category = shift;
my $example = shift;
@@ -146,6 +178,11 @@ sub handle_example_dir {
waitpid($pid, 0);
+ my $other_examples_html = "<ul>";
+ foreach my $example (get_examples_for_category($category)) {
+ $other_examples_html .= "<li><a href='/$category/$example'>$category/$example</a></li>";
+ }
+ $other_examples_html .= "</ul>";
my $html = '';
open my $htmltemplate, '<', "$examples_dir/template.html" or die("Couldn't open '$examples_dir/template.html': $!\n");
@@ -156,6 +193,7 @@ sub handle_example_dir {
s/\@javascript_file\@/$jsfname/g;
s/\@htmlified_source_code\@/$htmlified_source_code/g;
s/\@description\@/$description/g;
+ s/\@other_examples_html\@/$other_examples_html/g;
$html .= $_;
}
close($htmltemplate);
@@ -168,8 +206,6 @@ sub handle_example_dir {
sub handle_category_dir {
my $category = shift;
- # !!! FIXME: this needs to generate a preview page for all the examples things in the category.
-
print("Category $category ...\n");
do_mkdir("$output_dir/$category");
@@ -183,6 +219,35 @@ sub handle_category_dir {
}
closedir($dh);
+
+ my $examples_list_html = "";
+ foreach my $example (get_examples_for_category($category)) {
+ # !!! FIXME: image
+ my $example_image_url = "https://placehold.co/600x400/png";
+ $examples_list_html .= "
+ <a href='/$category/$example'>
+ <div>
+ <img src='$example_image_url' />
+ <div>$category/$example</div>
+ </div>
+ </a>";
+ }
+
+ # write category page
+ my $dst = "$output_dir/$category";
+ my $html = '';
+ open my $htmltemplate, '<', "$examples_dir/template-category.html" or die("Couldn't open '$examples_dir/template-category.html': $!\n");
+ while (<$htmltemplate>) {
+ s/\@project_name\@/$project/g;
+ s/\@category_name\@/$category/g;
+ s/\@examples_list_html\@/$examples_list_html/g;
+ $html .= $_;
+ }
+ close($htmltemplate);
+
+ open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n");
+ print $htmloutput $html;
+ close($htmloutput);
}
@@ -210,6 +275,8 @@ sub handle_category_dir {
build_latest();
+do_copy("$examples_dir/template.css", "$output_dir/examples.css");
+
opendir(my $dh, $examples_dir) or die("Couldn't opendir '$examples_dir': $!\n");
while (readdir($dh)) {
@@ -221,6 +288,38 @@ sub handle_category_dir {
closedir($dh);
+# write homepage
+my $homepage_list_html = "";
+foreach my $category (get_categories()) {
+ $homepage_list_html .= "<h2>$category</h2>";
+ $homepage_list_html .= "<div class='list'>";
+ foreach my $example (get_examples_for_category($category)) {
+ # !!! FIXME: image
+ my $example_image_url = "https://placehold.co/600x400/png";
+ $homepage_list_html .= "
+ <a href='/$category/$example'>
+ <div>
+ <img src='$example_image_url' />
+ <div>$category/$example</div>
+ </div>
+ </a>";
+ }
+ $homepage_list_html .= "</div>";
+}
+
+my $dst = "$output_dir/";
+my $html = '';
+open my $htmltemplate, '<', "$examples_dir/template-homepage.html" or die("Couldn't open '$examples_dir/template-category.html': $!\n");
+while (<$htmltemplate>) {
+ s/\@project_name\@/$project/g;
+ s/\@homepage_list_html\@/$homepage_list_html/g;
+ $html .= $_;
+}
+close($htmltemplate);
+
+open my $htmloutput, '>', "$dst/index.html" or die("Couldn't open '$dst/index.html': $!\n");
+print $htmloutput $html;
+close($htmloutput);
+
print("All examples built successfully!\n");
exit(0); # success!
-
diff --git a/examples/template-category.html b/examples/template-category.html
new file mode 100644
index 0000000000000..bc79e8d834a8d
--- /dev/null
+++ b/examples/template-category.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <title>@project_name@ Examples: @category_name@</title>
+ <link rel="stylesheet" type="text/css" href="/examples.css" />
+ <style>
+ main > h1 {
+ margin-top: 0;
+ }
+ </style>
+ </head>
+ <body>
+ <header>
+ <a href="/">SDL Examples</a>
+ </header>
+ <main>
+ <nav class="breadcrumb">
+ <ul>
+ <li><a href="/">@project_name@</a></li>
+ <li><a href="/@category_name@">@category_name@</a></li>
+ </ul>
+ </nav>
+ <h1>@project_name@ examples: @category_name@</h1>
+ <div class="list">@examples_list_html@</div>
+ </main>
+ </body>
+</html>
diff --git a/examples/template-homepage.html b/examples/template-homepage.html
new file mode 100644
index 0000000000000..369237df9e460
--- /dev/null
+++ b/examples/template-homepage.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <title>@project_name@ Examples</title>
+ <link rel="stylesheet" type="text/css" href="/examples.css" />
+ <style>
+ main > h1 {
+ margin-top: 0;
+ }
+ </style>
+ </head>
+ <body>
+ <header>
+ <a href="/">SDL Examples</a>
+ </header>
+ <main>
+ <nav class="breadcrumb">
+ <ul>
+ <li><a href="/">@project_name@</a></li>
+ </ul>
+ </nav>
+ <h1>@project_name@ examples</h1>
+
+ <p>Check out the @project_name@ examples here!</p>
+
+ @homepage_list_html@
+ </main>
+ </body>
+</html>
diff --git a/examples/template.css b/examples/template.css
new file mode 100644
index 0000000000000..8e86723cd30aa
--- /dev/null
+++ b/examples/template.css
@@ -0,0 +1,284 @@
+/** from ghwikipp.css */
+:root {
+ color-scheme: dark light; /* both supported */
+}
+
+body {
+ background-color: white;
+ padding: 2vw;
+ color: #333;
+ max-width: 1200px;
+ margin: 0 auto;
+ font-size: 16px;
+ line-height: 1.5;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans",
+ Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
+ overflow-wrap: break-word;
+}
+
+a {
+ color: #0969da;
+ /* text-decoration: none; */
+}
+
+a:visited {
+ color: #064998;
+}
+
+h1 {
+ border-bottom: 2px solid #efefef;
+}
+
+h2 {
+ border-bottom: 1px solid #efefef;
+}
+
+p {
+ max-width: 85ch;
+}
+
+li {
+ max-width: 85ch;
+}
+
+div.sourceCode {
+ background-color: #f6f8fa;
+ max-width: 100%;
+ padding: 16px;
+}
+
+code {
+ background-color: #f6f8fa;
+ padding: 0px;
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
+ "Liberation Mono", monospace;
+}
+
+table {
+ border: 1px solid #808080;
+ border-collapse: collapse;
+}
+
+td {
+ border: 1px solid #808080;
+ padding: 5px;
+}
+
+tr:nth-child(even) {
+ background-color: #f6f8fa;
+}
+
+.wikitopbanner {
+ background-color: #efefef;
+ padding: 10px;
+ margin-bottom: 10px;
+ width: auto;
+}
+
+.wikibottombanner {
+ background-color: #efefef;
+ padding: 10px;
+ margin-top: 10px;
+ width: auto;
+}
+
+.alertBox {
+ background-color: #f8d7da;
+ border: 1px solid #f5c6cb;
+ max-width: 60%;
+ padding: 10;
+ margin: auto;
+}
+
+.anchorImage {
+ visibility: hidden;
+ padding-left: 0.2em;
+ color: #fff;
+}
+
+.anchorText:hover .anchorImage {
+ visibility: visible;
+}
+
+hr {
+ display: block;
+ height: 1px;
+ border: 0;
+ border-top: 1px solid #efefef;
+ margin: 1em 0;
+ padding: 0;
+}
+
+/* Text and background color for dark mode */
+@media (prefers-color-scheme: dark) {
+ body {
+ color: #e6edf3;
+ background-color: #0d1117;
+ }
+
+ h1 {
+ border-color: rgba(48, 54, 61, 0.7);
+ }
+
+ h2 {
+ border-color: rgba(48, 54, 61, 0.7);
+ }
+
+ hr {
+ border-color: rgba(48, 54, 61, 0.7);
+ }
+
+ div.sourceCode {
+ background-color: #161b22;
+ }
+
+ code {
+ background-color: #161b22;
+ }
+
+ a {
+ color: #4493f8;
+ }
+
+ a:visited {
+ color: #2f66ad;
+ }
+
+ table {
+ border-color: rgba(48, 54, 61, 0.7);
+ }
+
+ td {
+ border-color: rgba(48, 54, 61, 0.7);
+ }
+
+ tr:nth-child(even) {
+ background-color: #161b22;
+ }
+
+ .wikitopbanner {
+ background-color: #263040;
+ }
+
+ .wikibottombanner {
+ background-color: #263040;
+ }
+
+ .anchorText:hover .anchorImage {
+ filter: invert(100%);
+ }
+}
+
+@media print {
+ body {
+ font-size: 12px;
+ }
+
+ table {
+ font-size: inherit;
+ }
+
+ a:visited {
+ color: #0969da;
+ }
+
+ .wikitopbanner,
+ .anchorText,
+ .wikibottombanner {
+ display: none;
+ }
+}
+
+/** additional (& overrides) for examples */
+header {
+ background-color: #efefef;
+ padding: 10px;
+ font-size: 2rem;
+}
+
+header > a,
+header > a:hover,
+header > a:visited {
+ color: inherit;
+ text-decoration: none;
+}
+
+.breadcrumb {
+ padding: 0.75rem 0.75rem;
+}
+
+.breadcrumb ul {
+ display: flex;
+ flex-wrap: wrap;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.breadcrumb li:not(:last-child)::after {
+ display: inline-block;
+ margin: 0 0.25rem;
+ content: "ยป";
+}
+
+.list {
+ display: flex;
+ flex-flow: row wrap;
+ gap: 24px;
+}
+
+.list > a > div {
+ width: 200px;
+ border: 5px solid #efefef;
+ border-radius: 5px;
+ background: #efefef;
+
+ display: flex;
+ flex-flow: column nowrap;
+
+ transition: border 0.25s;
+}
+
+.list > a > div:hover {
+ border-color: #064998;
+}
+
+.list > a > div > img {
+ width: 100%;
+ border-radius: 5px;
+}
+
+.list > a > div > div {
+ text-align: center;
+}
+
+.list > a,
+.list > a:visited {
+ display: block;
+ color: inherit;
+ text-decoration: none;
+}
+.list > a:hover {
+ color: #0969da;
+}
+
+@media (prefers-color-scheme: dark) {
+ header {
+ background-color: #263040;
+ }
+
+ .breadcrumb li:not(:last-child)::after {
+ color: #efefef;
+ }
+
+ .list > a > div {
+ border-color: #333;
+ background: #333;
+ }
+}
+
+@media only screen and (max-width: 992px) {
+ .list > a > div {
+ width: 150px;
+ }
+}
diff --git a/examples/template.html b/examples/template.html
index e8be678b31fe5..caf6dccca456c 100644
--- a/examples/template.html
+++ b/examples/template.html
@@ -3,27 +3,48 @@
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>@project_name@ Example: @category_name@/@example_name@</title>
+ <link rel="stylesheet" type="text/css" href="/examples.css" />
<style>
- html, body {
- width: 100vw;
- height: 100vh;
- overflow: hidden;
- font-family: 'Liberation Sans', sans-serif;
+ main {
+ display: flex;
}
- .canvas-container {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
+ main > #sidebar {
+ flex: 0 1 25%;
+ border-left: 2px solid #efefef;
+ padding: 1rem 1rem;
+ }
+
+ main > #content {
+ flex: 1 1 auto;
+ margin-bottom: 16px;
+ }
+
+ main > #content > h1 {
+ margin-top: 0;
+ }
+
+ main > #sidebar ul {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ }
+ main > #sidebar li {
+ padding: 2px 0;
+ }
+
+ #example-description {
+ max-width: 85ch;
+ margin-bottom: 16px;
+ }
+
+ .canvas-container {
display: flex;
align-items: center;
justify-content: center;
-
- background: black;
}
#canvas {
@@ -31,7 +52,7 @@
}
#output-container {
- position: absolute;
+ position: fixed;
top: 100%;
left: 0;
right: 0;
@@ -47,8 +68,8 @@
}
#output-container::before {
- position: absolute;
- bottom: 100%;
+ position: fixed;
+ bottom: 0;
right: 1rem;
content: 'Console';
@@ -87,11 +108,12 @@
outline: none;
resize: none;
- font-family: 'Lucida Console', Monaco, monospace;
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
+ "Liberation Mono", monospace;
}
#source-code {
- position: absolute;
+ position: fixed;
top: 100%;
left: 0;
right: 0;
@@ -104,8 +126,8 @@
}
#source-code::before {
- position: absolute;
- bottom: 100%;
+ position: fixed;
+ bottom: 0;
left: 1rem;
content: 'Source code';
@@ -134,21 +156,53 @@
overflow: scroll;
}
- #example-description {
- color: white;
- text-align: center;
- position: relative; /* required for proper positioning */
+ @media (prefers-color-scheme: dark) {
+ main > #sidebar {
+ border-color: rgba(48, 54, 61, 0.7);
+ }
+ }
+
+ @media only screen and (max-width: 992px) {
+ main {
+ flex-direction: column;
+ }
+
+ main > #sidebar {
+ border: none;
+ }
}
</style>
<link rel="stylesheet" type="text/css" href="highlight.css">
</head>
<body>
- <div class="canvas-container">
- <canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
- </div>
- <div id="example-description">
- @description@
- </div>
+ <header>
+ <a href="/">SDL Examples</a>
+ </header>
+ <main>
+ <div id="content">
+ <nav class="breadcrumb">
+ <ul>
+ <li><a href="/">@project_name@</a></li>
+ <li><a href="/@category_name@">@category_name@</a></li>
+ <li><a href="/@category_name@/@example_name@">@example_name@</a></li>
+ </ul>
+ </nav>
+ <h1>@project_name@ example: @category_name@/@example_name@</h1>
+ <div id="example-description">@description@</div>
+ <div class="canvas-container">
+ <canvas
+ id="canvas"
+ oncontextmenu="event.preventDefault()"
+ tabindex="-1"
+ ></canvas>
+ </div>
+ </div>
+ <div id="sidebar">
+ <h3>Other examples:</h3>
+ @other_examples_html@
+ </div>
+ </main>
+
<div id="output-container">
<textarea id="output" rows="8" spellcheck="false" readonly></textarea>
</div>