@@ -35,10 +35,13 @@ import storage from '../lib/storage';
35
35
import vmListenerHOC from '../lib/vm-listener-hoc.jsx' ;
36
36
import vmManagerHOC from '../lib/vm-manager-hoc.jsx' ;
37
37
import cloudManagerHOC from '../lib/cloud-manager-hoc.jsx' ;
38
+ import Loader from '../components/loader/loader.jsx' ;
38
39
39
- import GUIComponent from '../components/gui/gui.jsx' ;
40
+ // import GUIComponent from '../components/gui/gui.jsx';
40
41
import { setIsScratchDesktop } from '../lib/isScratchDesktop.js' ;
41
42
43
+ import VideoProvider from '../lib/video/video-provider' ;
44
+
42
45
const messages = defineMessages ( {
43
46
defaultProjectTitle : {
44
47
id : 'gui.gui.defaultProjectTitle' ,
@@ -52,6 +55,22 @@ class GUI extends React.Component {
52
55
setIsScratchDesktop ( this . props . isScratchDesktop ) ;
53
56
this . setReduxTitle ( this . props . projectTitle ) ;
54
57
this . props . onStorageInit ( storage ) ;
58
+
59
+ // Use setTimeout. Do not use requestAnimationFrame or a resolved
60
+ // Promise. We want this work delayed until after the data request is
61
+ // made.
62
+ setTimeout ( this . ensureRenderer . bind ( this ) ) ;
63
+
64
+ // Once the GUI component has been rendered, always render GUI and do
65
+ // not revert back to a Loader in this component.
66
+ //
67
+ // This makes GUI container not a pure component. We don't want to use
68
+ // state for this. That would possibly cause a full second render of GUI
69
+ // after the first one.
70
+ const { fontsLoaded, fetchingProject, isLoading} = this . props ;
71
+ this . isAfterGUI = this . isAfterGUI || (
72
+ fontsLoaded && ! fetchingProject && ! isLoading
73
+ ) ;
55
74
}
56
75
componentDidUpdate ( prevProps ) {
57
76
if ( this . props . projectId !== prevProps . projectId && this . props . projectId !== null ) {
@@ -65,6 +84,17 @@ class GUI extends React.Component {
65
84
// At this time the project view in www doesn't need to know when a project is unloaded
66
85
this . props . onProjectLoaded ( ) ;
67
86
}
87
+
88
+ // Once the GUI component has been rendered, always render GUI and do
89
+ // not revert back to a Loader in this component.
90
+ //
91
+ // This makes GUI container not a pure component. We don't want to use
92
+ // state for this. That would possibly cause a full second render of GUI
93
+ // after the first one.
94
+ const { fontsLoaded, fetchingProject, isLoading} = this . props ;
95
+ this . isAfterGUI = this . isAfterGUI || (
96
+ fontsLoaded && ! fetchingProject && ! isLoading
97
+ ) ;
68
98
}
69
99
setReduxTitle ( newTitle ) {
70
100
if ( newTitle === null || typeof newTitle === 'undefined' ) {
@@ -75,6 +105,36 @@ class GUI extends React.Component {
75
105
this . props . onUpdateReduxProjectTitle ( newTitle ) ;
76
106
}
77
107
}
108
+ ensureRenderer ( ) {
109
+ if ( this . props . vm . renderer ) {
110
+ return ;
111
+ }
112
+
113
+ // Wait to load svg-renderer and render after the data request. This
114
+ // way the data request is made earlier.
115
+ const Renderer = require ( 'scratch-render' ) ;
116
+ const {
117
+ SVGRenderer : V2SVGAdapter ,
118
+ BitmapAdapter : V2BitmapAdapter
119
+ } = require ( 'scratch-svg-renderer' ) ;
120
+
121
+ const vm = this . props . vm ;
122
+ this . canvas = document . createElement ( 'canvas' ) ;
123
+ this . renderer = new Renderer ( this . canvas ) ;
124
+ vm . attachRenderer ( this . renderer ) ;
125
+
126
+ vm . attachV2SVGAdapter ( new V2SVGAdapter ( ) ) ;
127
+ vm . attachV2BitmapAdapter ( new V2BitmapAdapter ( ) ) ;
128
+
129
+ // Only attach a video provider once because it is stateful
130
+ vm . setVideoProvider ( new VideoProvider ( ) ) ;
131
+
132
+ // Calling draw a single time before any project is loaded just
133
+ // makes the canvas white instead of solid black–needed because it
134
+ // is not possible to use CSS to style the canvas to have a
135
+ // different default color
136
+ vm . renderer . draw ( ) ;
137
+ }
78
138
render ( ) {
79
139
if ( this . props . isError ) {
80
140
throw new Error (
@@ -85,6 +145,7 @@ class GUI extends React.Component {
85
145
assetHost,
86
146
cloudHost,
87
147
error,
148
+ fontsLoaded,
88
149
isError,
89
150
isScratchDesktop,
90
151
isShowingProject,
@@ -102,6 +163,16 @@ class GUI extends React.Component {
102
163
loadingStateVisible,
103
164
...componentProps
104
165
} = this . props ;
166
+
167
+ if ( ! this . isAfterGUI && (
168
+ ! fontsLoaded || fetchingProject || isLoading
169
+ ) ) {
170
+ // Make sure a renderer exists.
171
+ if ( fontsLoaded && ! fetchingProject ) this . ensureRenderer ( ) ;
172
+ return < Loader /> ;
173
+ }
174
+
175
+ const GUIComponent = require ( '../components/gui/gui.jsx' ) . default ;
105
176
return (
106
177
< GUIComponent
107
178
loading = { fetchingProject || isLoading || loadingStateVisible }
@@ -119,6 +190,7 @@ GUI.propTypes = {
119
190
cloudHost : PropTypes . string ,
120
191
error : PropTypes . oneOfType ( [ PropTypes . object , PropTypes . string ] ) ,
121
192
fetchingProject : PropTypes . bool ,
193
+ fontsLoaded : PropTypes . bool ,
122
194
intl : intlShape ,
123
195
isError : PropTypes . bool ,
124
196
isLoading : PropTypes . bool ,
@@ -157,6 +229,7 @@ const mapStateToProps = state => {
157
229
costumeLibraryVisible : state . scratchGui . modals . costumeLibrary ,
158
230
costumesTabVisible : state . scratchGui . editorTab . activeTabIndex === COSTUMES_TAB_INDEX ,
159
231
error : state . scratchGui . projectState . error ,
232
+ fontsLoaded : state . scratchGui . fontsLoaded ,
160
233
isError : getIsError ( loadingState ) ,
161
234
isFullScreen : state . scratchGui . mode . isFullScreen ,
162
235
isPlayerOnly : state . scratchGui . mode . isPlayerOnly ,
0 commit comments