Skip to content

Commit b161942

Browse files
committed
Works & tested on the platforms Windows, Linux and MacOS. Minimal Resolution 1600 x 900.
1 parent b5d969b commit b161942

File tree

7 files changed

+157
-27
lines changed

7 files changed

+157
-27
lines changed

Lessons/.gdignore

Whitespace-only changes.

data/core/environment/EnvironmentPaths.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,47 @@ namespace Osls.Environment
33
public static class EnvironmentPaths
44
{
55
/// <summary>
6-
/// Gets the globalized lesson folder path
6+
/// Gets the forced globalized lesson folder path.
7+
/// ProjectSettings.GlobalizePath will not return the system path for res:// in export
8+
/// Currently there seems to be no way to get the execution folder directly,
9+
/// but we can use the exe path but only in export mode.
710
/// </summary>
811
public static string LessonsFolderPath
912
{
1013
get
1114
{
12-
return Godot.ProjectSettings.GlobalizePath("res://lessons/");
15+
string path = Godot.ProjectSettings.GlobalizePath(InternalLessonsFolderPath);
16+
if (path.Contains(":"))
17+
{
18+
return path;
19+
}
20+
path = Godot.OS.GetExecutablePath();
21+
int lastIndex = path.LastIndexOf("/");
22+
path = path.Substring(0, lastIndex + 1);
23+
return path + "lessons/";
24+
}
25+
}
26+
27+
/// <summary>
28+
/// Gets the globalized alternative lesson folder path if the regular does not work.
29+
/// Especially used for MacOS as the res path is not writeable.
30+
/// </summary>
31+
public static string LessonsFolderPathAlternative
32+
{
33+
get
34+
{
35+
return Godot.ProjectSettings.GlobalizePath("user://lessons/");
36+
}
37+
}
38+
39+
/// <summary>
40+
/// Gets the internal packed lesson folder path. Read only.
41+
/// </summary>
42+
public static string InternalLessonsFolderPath
43+
{
44+
get
45+
{
46+
return "res://lessons/";
1347
}
1448
}
1549
}

data/core/landing_page/LandingPage.tscn

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
[gd_scene load_steps=6 format=2]
1+
[gd_scene load_steps=7 format=2]
22

33
[ext_resource path="res://data/core/landing_page/LandingPageNode.cs" type="Script" id=1]
44
[ext_resource path="res://data/core/landing_page/LessonsNode.cs" type="Script" id=2]
55
[ext_resource path="res://data/core/landing_page/LessonSelectionGridNode.cs" type="Script" id=3]
66
[ext_resource path="res://data/core/lesson/viewer/LessonView.tscn" type="PackedScene" id=4]
77
[ext_resource path="res://data/core/landing_page/lesson_source_controller/images/DiagramScreenshot.png" type="Texture" id=5]
8+
[ext_resource path="res://data/core/landing_page/lesson_source_controller/LessonController.tscn" type="PackedScene" id=6]
89

910
[node name="LandingPage" type="Node"]
1011
script = ExtResource( 1 )
@@ -19,6 +20,9 @@ margin_left = 80.0
1920
margin_top = 1.0
2021
margin_right = -513.0
2122
rect_clip_content = true
23+
__meta__ = {
24+
"_edit_use_anchors_": false
25+
}
2226

2327
[node name="Background" type="TextureRect" parent="LessonsNode/LessonSelectorContainer"]
2428
modulate = Color( 0.956863, 0.94902, 0.917647, 1 )
@@ -53,6 +57,7 @@ custom_constants/vseparation = 50
5357
custom_constants/hseparation = 50
5458
columns = 4
5559
script = ExtResource( 3 )
60+
_lessonControllerScene = ExtResource( 6 )
5661

5762
[node name="LessonPreviewBackground" type="ColorRect" parent="LessonsNode"]
5863
anchor_left = 1.0

data/core/landing_page/LessonSelectionGridNode.cs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Godot;
2-
using Osls.Environment;
32

43

54
namespace Osls.LandingPage
@@ -11,7 +10,7 @@ namespace Osls.LandingPage
1110
public class LessonSelectionGridNode : GridContainer
1211
{
1312
#region ==================== Fields ====================
14-
private const string LessonControllerScene = "res://data/core/landing_page/lesson_source_controller/LessonController.tscn";
13+
[Export] private PackedScene _lessonControllerScene;
1514
private LessonControllerNode[] _lessonNodes;
1615
#endregion
1716

@@ -21,7 +20,7 @@ public override void _Ready()
2120
{
2221
try
2322
{
24-
LessonEntity[] lessons = LoadLessons();
23+
LessonEntity[] lessons = LessonCollector.LoadLessons();
2524
CreateButtons(lessons);
2625
}
2726
catch (System.Exception e)
@@ -57,28 +56,13 @@ public void StartSelectedLesson(LessonControllerNode node)
5756
#endregion
5857

5958

60-
#region ==================== Private Methods ====================
61-
private LessonEntity[] LoadLessons()
62-
{
63-
string[] LessonPaths = System.IO.Directory.GetDirectories(EnvironmentPaths.LessonsFolderPath);
64-
System.Array.Sort<string>(LessonPaths);
65-
LessonEntity[] lessonEntities = new LessonEntity[LessonPaths.Length];
66-
for (int i = 0; i < LessonPaths.Length; i++)
67-
{
68-
LessonEntity lessonData = new LessonEntity(LessonPaths[i]);
69-
lessonData.LoadFolderContent();
70-
lessonEntities[i] = lessonData;
71-
}
72-
return lessonEntities;
73-
}
74-
59+
#region ==================== Helpers ====================
7560
private void CreateButtons(LessonEntity[] lessons)
7661
{
77-
PackedScene packedControllerNode = (PackedScene)GD.Load(LessonControllerScene);
7862
_lessonNodes = new LessonControllerNode[lessons.Length];
7963
for (int i = 0; i < lessons.Length; i++)
8064
{
81-
LessonControllerNode controllerNode = (LessonControllerNode)packedControllerNode.Instance();
65+
LessonControllerNode controllerNode = (LessonControllerNode)_lessonControllerScene.Instance();
8266
controllerNode.SetLessonInfo(lessons[i], this);
8367
AddChild(controllerNode);
8468
_lessonNodes[i] = controllerNode;

data/core/lesson/LessonCollector.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
using Godot;
2+
using Osls.Environment;
3+
4+
5+
namespace Osls
6+
{
7+
public static class LessonCollector
8+
{
9+
#region ==================== Public Methods ====================
10+
/// <summary>
11+
/// Loads all aviable lessons at the locations:
12+
/// res://lessons/ and as an alternative user://lesons
13+
/// If it does not exist we try to create a default directory at
14+
/// res://lessons/ and as an alternative user://lesons
15+
/// </summary>
16+
public static LessonEntity[] LoadLessons()
17+
{
18+
string[] LessonPaths = GetLessonPaths();
19+
System.Array.Sort<string>(LessonPaths);
20+
LessonEntity[] lessonEntities = new LessonEntity[LessonPaths.Length];
21+
for (int i = 0; i < LessonPaths.Length; i++)
22+
{
23+
LessonEntity lessonData = new LessonEntity(LessonPaths[i]);
24+
lessonData.LoadFolderContent();
25+
lessonEntities[i] = lessonData;
26+
}
27+
return lessonEntities;
28+
}
29+
#endregion
30+
31+
32+
#region ==================== Helpers ====================
33+
/// <summary>
34+
/// Gets the base directories for the lesson folders.
35+
/// It first looks under the res folder and then in the user path.
36+
/// If none could be found we create a new dir and copy the default data in it.
37+
/// </summary>
38+
private static string[] GetLessonPaths()
39+
{
40+
string[] LessonPaths = TryLoad(EnvironmentPaths.LessonsFolderPath);
41+
if (LessonPaths != null && LessonPaths.Length > 0) return LessonPaths;
42+
LessonPaths = TryLoad(EnvironmentPaths.LessonsFolderPathAlternative);
43+
if (LessonPaths != null && LessonPaths.Length > 0) return LessonPaths;
44+
if (CreateDefault(EnvironmentPaths.LessonsFolderPath))
45+
{
46+
return TryLoad(EnvironmentPaths.LessonsFolderPath);
47+
}
48+
if (CreateDefault(EnvironmentPaths.LessonsFolderPathAlternative))
49+
{
50+
return TryLoad(EnvironmentPaths.LessonsFolderPathAlternative);
51+
}
52+
GD.PushWarning("Could not find any lessons at: " + EnvironmentPaths.LessonsFolderPath + " and " + EnvironmentPaths.LessonsFolderPathAlternative);
53+
return new string[0];
54+
}
55+
56+
private static string[] TryLoad(string path)
57+
{
58+
if (!System.IO.Directory.Exists(path)) return new string[0];
59+
return System.IO.Directory.GetDirectories(path);
60+
}
61+
62+
/// <summary>
63+
/// Returns true if the default files could be copied to the base path
64+
/// </summary>
65+
private static bool CreateDefault(string basePath)
66+
{
67+
GD.Print("Try to copy " + EnvironmentPaths.InternalLessonsFolderPath + " to " + basePath);
68+
Directory targetDir = new Directory();
69+
Directory internalDir = new Directory();
70+
Error result = targetDir.MakeDirRecursive(basePath);
71+
Error internalresult = internalDir.Open(EnvironmentPaths.InternalLessonsFolderPath);
72+
if (result != Error.Ok || internalresult != Error.Ok) return false;
73+
ProcessDirectory(EnvironmentPaths.InternalLessonsFolderPath, basePath);
74+
return true;
75+
}
76+
77+
/// <summary>
78+
/// Copies all files from the given internal directory to the given path.
79+
/// As the internal data is packed we need to use godots directory system.
80+
/// </summary>
81+
private static void ProcessDirectory(string internalPath, string externalPath)
82+
{
83+
Directory internalDir = new Directory();
84+
Error result = internalDir.Open(internalPath);
85+
if (result != Error.Ok) return;
86+
internalDir.ListDirBegin(true, true);
87+
while (true)
88+
{
89+
string item = internalDir.GetNext();
90+
if (string.IsNullOrEmpty(item)) return;
91+
if (internalDir.CurrentIsDir())
92+
{
93+
Directory targetDir = new Directory();
94+
GD.Print("create " + externalPath + item + "/");
95+
targetDir.MakeDir(externalPath + item + "/");
96+
ProcessDirectory(internalPath + item + "/", externalPath + item + "/");
97+
}
98+
else
99+
{
100+
GD.Print("copy " + internalPath + item + " to " + externalPath + item);
101+
internalDir.Copy(internalPath + item, externalPath + item);
102+
}
103+
}
104+
}
105+
#endregion
106+
}
107+
}

data/core/lesson/LessonEntity.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void SetAndSaveStars(int stars)
118118
#region ==================== Helpers ====================
119119
/// <summary>
120120
/// Loads the description json as a godot dictionary.
121-
/// It is different than the System.Runtime.Serialization json attribute deserilisation.
121+
/// It is different than the System.Runtime.Serialization json attribute deserialization.
122122
/// But it is sufficient for our needs.
123123
/// </summary>
124124
private void LoadDescriptionDictionary()

export_presets.cfg

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ platform="Windows Desktop"
55
runnable=true
66
custom_features=""
77
export_filter="all_resources"
8-
include_filter=""
8+
include_filter="lessons/*"
99
exclude_filter=""
1010
export_path="../Export/Windows/Open Sequential Logic Simulation.exe"
1111
script_export_mode=1
@@ -47,7 +47,7 @@ platform="Linux/X11"
4747
runnable=true
4848
custom_features=""
4949
export_filter="all_resources"
50-
include_filter=""
50+
include_filter="lessons/*"
5151
exclude_filter=""
5252
export_path="../Export/Linux/Open Sequential Logic Simulation.x86_64"
5353
script_export_mode=1
@@ -72,7 +72,7 @@ platform="Mac OSX"
7272
runnable=true
7373
custom_features=""
7474
export_filter="all_resources"
75-
include_filter=""
75+
include_filter="lessons/*"
7676
exclude_filter=""
7777
export_path="../Export/MacOS/Open Sequential Logic Simulation.zip"
7878
script_export_mode=1

0 commit comments

Comments
 (0)