mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Merge pull request #742 from 4ian/feature/font-resource
Rework fonts loading
This commit is contained in:
@@ -370,8 +370,8 @@
|
||||
<Unit filename="GDCore/IDE/Events/ExpressionsCorrectnessTesting.h" />
|
||||
<Unit filename="GDCore/IDE/ExtensionsLoader.cpp" />
|
||||
<Unit filename="GDCore/IDE/ExtensionsLoader.h" />
|
||||
<Unit filename="GDCore/IDE/Project/ImagesUsedInventorizer.cpp" />
|
||||
<Unit filename="GDCore/IDE/Project/ImagesUsedInventorizer.h" />
|
||||
<Unit filename="GDCore/IDE/Project/ResourcesInUseHelper.cpp" />
|
||||
<Unit filename="GDCore/IDE/Project/ResourcesInUseHelper.h" />
|
||||
<Unit filename="GDCore/Extensions/Metadata/MetadataProvider.cpp" />
|
||||
<Unit filename="GDCore/Extensions/Metadata/MetadataProvider.h" />
|
||||
<Unit filename="GDCore/IDE/PlatformLoader.cpp" />
|
||||
|
@@ -41,7 +41,7 @@
|
||||
#include "GDCore/IDE/Dialogs/DndResourcesEditor.h"
|
||||
#include "GDCore/IDE/Dialogs/PropertyDescriptor.h"
|
||||
#include "GDCore/IDE/Dialogs/ResourceLibraryDialog.h"
|
||||
#include "GDCore/IDE/Project/ImagesUsedInventorizer.h"
|
||||
#include "GDCore/IDE/Project/ResourcesInUseHelper.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesAdder.h"
|
||||
#include "GDCore/IDE/wxTools/FileProperty.h"
|
||||
#include "GDCore/IDE/wxTools/SkinHelper.h"
|
||||
@@ -1332,7 +1332,7 @@ void ResourcesEditor::Refresh() {
|
||||
|
||||
void ResourcesEditor::OnDeleteUnusedFiles(wxCommandEvent& event) {
|
||||
std::vector<gd::String> unusedImages =
|
||||
gd::ProjectResourcesAdder::GetAllUselessImages(project);
|
||||
gd::ProjectResourcesAdder::GetAllUseless(project, "image");
|
||||
|
||||
// Construct corresponding wxArrayString with unused images
|
||||
wxArrayString imagesNotUsed;
|
||||
|
@@ -21,7 +21,7 @@ using namespace std;
|
||||
namespace gd {
|
||||
|
||||
void ArbitraryResourceWorker::ExposeImage(gd::String& imageName){
|
||||
// Nothing to do, the image is a referece to a resource that
|
||||
// Nothing to do, the image is a reference to a resource that
|
||||
// is already exposed.
|
||||
};
|
||||
|
||||
@@ -40,6 +40,21 @@ void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
|
||||
ExposeFile(audioName);
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
|
||||
for (auto resources : GetResources()) {
|
||||
if (!resources) continue;
|
||||
|
||||
if (resources->HasResource(fontName) &&
|
||||
resources->GetResource(fontName).GetKind() == "font") {
|
||||
// Nothing to do, the font is a reference to a resource that
|
||||
// is already exposed.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ExposeFile(fontName);
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeResources(
|
||||
gd::ResourcesManager* resourcesManager) {
|
||||
if (!resourcesManager) return;
|
||||
|
@@ -34,7 +34,7 @@ namespace gd {
|
||||
* sometimes update them.
|
||||
*
|
||||
* \see ResourcesMergingHelper
|
||||
* \see gd::ImagesUsedInventorizer
|
||||
* \see gd::ResourcesInUseHelper
|
||||
*
|
||||
* \see gd::LaunchResourceWorkerOnEvents
|
||||
*
|
||||
@@ -64,6 +64,12 @@ class GD_CORE_API ArbitraryResourceWorker {
|
||||
*/
|
||||
virtual void ExposeAudio(gd::String &audioName);
|
||||
|
||||
/**
|
||||
* \brief Expose a font, which is either a reference to a "font" resource,
|
||||
* or a filename if no resource with this name exists.
|
||||
*/
|
||||
virtual void ExposeFont(gd::String &fontName);
|
||||
|
||||
/**
|
||||
* \brief Expose a shader.
|
||||
* \warn Currently unsupported.
|
||||
|
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#ifndef IMAGESUSEDINVENTORIZER_H
|
||||
#define IMAGESUSEDINVENTORIZER_H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Class used to track all images used in a game.
|
||||
*
|
||||
* Usage example:
|
||||
\code
|
||||
gd::ImagesUsedInventorizer inventorizer;
|
||||
project.ExposeResources(inventorizer);
|
||||
|
||||
//Get a set with the name of all images in the project:
|
||||
std::set<gd::String> & usedImages = inventorizer.GetAllUsedImages();
|
||||
\endcode
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class ImagesUsedInventorizer : public gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
ImagesUsedInventorizer() : gd::ArbitraryResourceWorker(){};
|
||||
virtual ~ImagesUsedInventorizer(){};
|
||||
|
||||
std::set<gd::String>& GetAllUsedImages() { return allUsedImages; };
|
||||
|
||||
virtual void ExposeFile(gd::String& resource){
|
||||
/*Don't care, we just list images*/};
|
||||
virtual void ExposeImage(gd::String& imageName) {
|
||||
allUsedImages.insert(imageName);
|
||||
};
|
||||
|
||||
protected:
|
||||
std::set<gd::String> allUsedImages;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // IMAGESUSEDINVENTORIZER_H
|
||||
#endif
|
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
#include "ProjectResourcesAdder.h"
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/IDE/Project/ImagesUsedInventorizer.h"
|
||||
#include "GDCore/IDE/Project/ResourcesInUseHelper.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
@@ -14,51 +14,52 @@ using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
bool ProjectResourcesAdder::AddAllMissingImages(gd::Project& project) {
|
||||
gd::ImagesUsedInventorizer inventorizer;
|
||||
project.ExposeResources(inventorizer);
|
||||
std::set<gd::String>& allImages = inventorizer.GetAllUsedImages();
|
||||
bool ProjectResourcesAdder::AddAllMissing(gd::Project& project,
|
||||
const gd::String& resourceType) {
|
||||
// Search for resources used in the project
|
||||
gd::ResourcesInUseHelper resourcesInUse;
|
||||
project.ExposeResources(resourcesInUse);
|
||||
|
||||
ResourcesManager& resourcesManager = project.GetResourcesManager();
|
||||
for (std::set<gd::String>::const_iterator it = allImages.begin();
|
||||
it != allImages.end();
|
||||
++it) {
|
||||
if (!resourcesManager.HasResource(*it)) {
|
||||
std::cout << "Adding missing resource \"" << *it << "\"to the project.";
|
||||
resourcesManager.AddResource(*it, /*filename=*/*it, "image");
|
||||
for (auto& resourceName : resourcesInUse.GetAll(resourceType)) {
|
||||
if (!resourcesManager.HasResource(resourceName)) {
|
||||
std::cout << "Adding missing resource \"" << resourceName
|
||||
<< "\"to the project." << std::endl;
|
||||
resourcesManager.AddResource(
|
||||
resourceName, /*filename=*/resourceName, resourceType);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<gd::String> ProjectResourcesAdder::GetAllUselessImages(
|
||||
gd::Project& project) {
|
||||
std::vector<gd::String> ProjectResourcesAdder::GetAllUseless(
|
||||
gd::Project& project, const gd::String& resourceType) {
|
||||
std::vector<gd::String> unusedResources;
|
||||
// Search for resources used in the project
|
||||
gd::ResourcesInUseHelper resourcesInUse;
|
||||
project.ExposeResources(resourcesInUse);
|
||||
std::set<gd::String>& usedResources = resourcesInUse.GetAll(resourceType);
|
||||
|
||||
// Search for used images
|
||||
gd::ImagesUsedInventorizer inventorizer;
|
||||
|
||||
project.ExposeResources(inventorizer);
|
||||
std::set<gd::String>& usedImages = inventorizer.GetAllUsedImages();
|
||||
|
||||
// Search all images resources not used
|
||||
// Search all resources not used
|
||||
std::vector<gd::String> resources =
|
||||
project.GetResourcesManager().GetAllResourceNames();
|
||||
for (std::size_t i = 0; i < resources.size(); i++) {
|
||||
if (project.GetResourcesManager().GetResource(resources[i]).GetKind() !=
|
||||
"image")
|
||||
resourceType)
|
||||
continue;
|
||||
|
||||
if (usedImages.find(resources[i]) == usedImages.end())
|
||||
if (usedResources.find(resources[i]) == usedResources.end())
|
||||
unusedResources.push_back(resources[i]);
|
||||
}
|
||||
|
||||
return unusedResources;
|
||||
}
|
||||
|
||||
void ProjectResourcesAdder::RemoveAllUselessImages(gd::Project& project) {
|
||||
std::vector<gd::String> unusedResources = GetAllUselessImages(project);
|
||||
void ProjectResourcesAdder::RemoveAllUseless(gd::Project& project,
|
||||
const gd::String& resourceType) {
|
||||
std::vector<gd::String> unusedResources =
|
||||
GetAllUseless(project, resourceType);
|
||||
|
||||
for (std::size_t i = 0; i < unusedResources.size(); ++i) {
|
||||
project.GetResourcesManager().RemoveResource(unusedResources[i]);
|
||||
|
@@ -21,38 +21,35 @@ namespace gd {
|
||||
class GD_CORE_API ProjectResourcesAdder {
|
||||
public:
|
||||
/**
|
||||
* \brief Update the project so that all missing images are added, with an
|
||||
* \brief Update the project so that all missing resources are added, with an
|
||||
* filename that is equal to the missing resource name.
|
||||
*
|
||||
* \param project The project to be updated.
|
||||
* \param resourceType The type of the resource the be searched
|
||||
*
|
||||
* \return true if no error happened
|
||||
*/
|
||||
static bool AddAllMissingImages(gd::Project& project);
|
||||
static bool AddAllMissing(gd::Project& project, const gd::String & resourceType);
|
||||
|
||||
/**
|
||||
* \brief Find all resources that are
|
||||
* not used in the project.
|
||||
*
|
||||
* \note For now, only images resources can be tracked and marked
|
||||
* as not used.
|
||||
* \brief Find all resources of the specified kind that are
|
||||
* not used by the project.
|
||||
*
|
||||
* \param project The project to be crawled.
|
||||
* \param resourceType The type of the resource the be searched
|
||||
*
|
||||
* \return A vector containing the name of all unused resources
|
||||
*/
|
||||
static std::vector<gd::String> GetAllUselessImages(gd::Project& project);
|
||||
static std::vector<gd::String> GetAllUseless(gd::Project& project, const gd::String & resourceType);
|
||||
|
||||
/**
|
||||
* \brief Remove all resources that are not used
|
||||
* in the project.
|
||||
*
|
||||
* \note For now, only images resources can be tracked and marked
|
||||
* as not used.
|
||||
* \brief Remove all resources of the specified kind that are not used
|
||||
* by the project.
|
||||
*
|
||||
* \param project The project to be crawled.
|
||||
* \param resourceType The type of the resource the be searched
|
||||
*/
|
||||
static void RemoveAllUselessImages(gd::Project& project);
|
||||
static void RemoveAllUseless(gd::Project& project, const gd::String & resourceType);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
72
Core/GDCore/IDE/Project/ResourcesInUseHelper.h
Normal file
72
Core/GDCore/IDE/Project/ResourcesInUseHelper.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#ifndef IMAGESUSEDINVENTORIZER_H
|
||||
#define IMAGESUSEDINVENTORIZER_H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Class used to track all resources used by a game,
|
||||
* or a part of it (like a gd::Object).
|
||||
*
|
||||
* Usage example:
|
||||
\code
|
||||
gd::ResourcesInUseHelper resourcesInUse;
|
||||
project.ExposeResources(resourcesInUse);
|
||||
|
||||
//Get a set with the name of all images in the project:
|
||||
std::set<gd::String> & usedImages = resourcesInUse.GetAllImages();
|
||||
\endcode
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourcesInUseHelper() : gd::ArbitraryResourceWorker(){};
|
||||
virtual ~ResourcesInUseHelper(){};
|
||||
|
||||
std::set<gd::String>& GetAllImages() { return GetAll("image"); };
|
||||
std::set<gd::String>& GetAllFonts() { return GetAll("font"); };
|
||||
std::set<gd::String>& GetAllAudios() { return GetAll("audio"); };
|
||||
std::set<gd::String>& GetAll(const gd::String& resourceType) {
|
||||
return resourceType == "image"
|
||||
? allImages
|
||||
: (resourceType == "audio"
|
||||
? allAudios
|
||||
: (resourceType == "font") ? allFonts : emptyResources);
|
||||
};
|
||||
|
||||
virtual void ExposeFile(gd::String& resource) override{
|
||||
/*Don't care, we just list resource names*/
|
||||
};
|
||||
virtual void ExposeImage(gd::String& imageResourceName) override {
|
||||
allImages.insert(imageResourceName);
|
||||
};
|
||||
virtual void ExposeAudio(gd::String& audioResourceName) override {
|
||||
allAudios.insert(audioResourceName);
|
||||
};
|
||||
virtual void ExposeFont(gd::String& fontResourceName) override {
|
||||
allFonts.insert(fontResourceName);
|
||||
};
|
||||
|
||||
protected:
|
||||
std::set<gd::String> allImages;
|
||||
std::set<gd::String> allAudios;
|
||||
std::set<gd::String> allFonts;
|
||||
std::set<gd::String> emptyResources;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // IMAGESUSEDINVENTORIZER_H
|
||||
#endif
|
@@ -79,6 +79,8 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
|
||||
return std::make_shared<ImageResource>();
|
||||
else if (kind == "audio")
|
||||
return std::make_shared<AudioResource>();
|
||||
else if (kind == "font")
|
||||
return std::make_shared<FontResource>();
|
||||
|
||||
std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
|
||||
return std::make_shared<Resource>();
|
||||
@@ -92,7 +94,7 @@ bool ResourcesManager::HasResource(const gd::String& name) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<gd::String> ResourcesManager::GetAllResourceNames() {
|
||||
std::vector<gd::String> ResourcesManager::GetAllResourceNames() const {
|
||||
std::vector<gd::String> allResources;
|
||||
for (std::size_t i = 0; i < resources.size(); ++i)
|
||||
allResources.push_back(resources[i]->GetName());
|
||||
@@ -134,19 +136,11 @@ bool ImageResource::UpdateProperty(const gd::String& name,
|
||||
bool ResourcesManager::AddResource(const gd::Resource& resource) {
|
||||
if (HasResource(resource.GetName())) return false;
|
||||
|
||||
try {
|
||||
const Resource& castedResource = dynamic_cast<const Resource&>(resource);
|
||||
std::shared_ptr<Resource> newResource =
|
||||
std::shared_ptr<Resource>(castedResource.Clone());
|
||||
if (newResource == std::shared_ptr<Resource>()) return false;
|
||||
|
||||
resources.push_back(newResource);
|
||||
} catch (...) {
|
||||
std::cout << "WARNING: Tried to add a resource which is not a GD C++ "
|
||||
"Platform Resource to a GD C++ Platform project";
|
||||
std::cout << char(7);
|
||||
}
|
||||
std::shared_ptr<Resource> newResource =
|
||||
std::shared_ptr<Resource>(resource.Clone());
|
||||
if (newResource == std::shared_ptr<Resource>()) return false;
|
||||
|
||||
resources.push_back(newResource);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -408,16 +402,8 @@ bool ResourceFolder::HasResource(const gd::String& name) const {
|
||||
|
||||
void ResourceFolder::AddResource(const gd::String& name,
|
||||
gd::ResourcesManager& parentManager) {
|
||||
try {
|
||||
ResourcesManager& manager = dynamic_cast<ResourcesManager&>(parentManager);
|
||||
std::shared_ptr<Resource> resource =
|
||||
std::dynamic_pointer_cast<Resource>(manager.GetResourceSPtr(name));
|
||||
if (resource != std::shared_ptr<Resource>()) resources.push_back(resource);
|
||||
} catch (...) {
|
||||
std::cout << "Warning: A resources manager which is not part of GD C++ "
|
||||
"Platform was used during call to AddResource"
|
||||
<< std::endl;
|
||||
}
|
||||
std::shared_ptr<Resource> resource = parentManager.GetResourceSPtr(name);
|
||||
if (resource != std::shared_ptr<Resource>()) resources.push_back(resource);
|
||||
}
|
||||
|
||||
void ResourcesManager::RenameResource(const gd::String& oldName,
|
||||
@@ -580,7 +566,31 @@ void AudioResource::SerializeTo(SerializerElement& element) const {
|
||||
"file", GetFile()); // Keep the resource path in the current locale (but
|
||||
// save it in UTF8 for compatibility on other OSes)
|
||||
}
|
||||
#endif
|
||||
|
||||
void FontResource::SetFile(const gd::String& newFile) {
|
||||
file = newFile;
|
||||
|
||||
// Convert all backslash to slashs.
|
||||
while (file.find('\\') != gd::String::npos)
|
||||
file.replace(file.find('\\'), 1, "/");
|
||||
}
|
||||
|
||||
void FontResource::UnserializeFrom(const SerializerElement& element) {
|
||||
SetUserAdded(element.GetBoolAttribute("userAdded"));
|
||||
SetFile(element.GetStringAttribute("file"));
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
void FontResource::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("userAdded", IsUserAdded());
|
||||
element.SetAttribute(
|
||||
"file", GetFile()); // Keep the resource path in the current locale (but
|
||||
// save it in UTF8 for compatibility on other OSes)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }
|
||||
|
||||
ResourceFolder& ResourceFolder::operator=(const ResourceFolder& other) {
|
||||
|
@@ -262,6 +262,34 @@ class GD_CORE_API AudioResource : public Resource {
|
||||
gd::String file;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Describe a font file used by a project.
|
||||
*
|
||||
* \see Resource
|
||||
* \ingroup ResourcesManagement
|
||||
*/
|
||||
class GD_CORE_API FontResource : public Resource {
|
||||
public:
|
||||
FontResource() : Resource() { SetKind("font"); };
|
||||
virtual ~FontResource(){};
|
||||
virtual FontResource* Clone() const override {
|
||||
return new FontResource(*this);
|
||||
}
|
||||
|
||||
virtual const gd::String& GetFile() const override { return file; };
|
||||
virtual void SetFile(const gd::String& newFile) override;
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual bool UseFile() override { return true; }
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
#endif
|
||||
|
||||
void UnserializeFrom(const SerializerElement& element) override;
|
||||
|
||||
private:
|
||||
gd::String file;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Inventory all resources used by a project
|
||||
*
|
||||
@@ -298,7 +326,7 @@ class GD_CORE_API ResourcesManager {
|
||||
/**
|
||||
* \brief Get a list containing the names of all resources.
|
||||
*/
|
||||
std::vector<gd::String> GetAllResourceNames();
|
||||
std::vector<gd::String> GetAllResourceNames() const;
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
|
@@ -87,11 +87,11 @@ TEST_CASE("Resources", "[common][resources]") {
|
||||
|
||||
SECTION("ProjectResourcesAdder") {
|
||||
std::vector<gd::String> uselessResources =
|
||||
gd::ProjectResourcesAdder::GetAllUselessImages(project);
|
||||
gd::ProjectResourcesAdder::GetAllUseless(project, "image");
|
||||
|
||||
REQUIRE(uselessResources.size() == 2);
|
||||
|
||||
gd::ProjectResourcesAdder::RemoveAllUselessImages(project);
|
||||
gd::ProjectResourcesAdder::RemoveAllUseless(project, "image");
|
||||
std::vector<gd::String> remainingResources =
|
||||
project.GetResourcesManager().GetAllResourceNames();
|
||||
REQUIRE(remainingResources.size() == 2);
|
||||
|
@@ -35,7 +35,7 @@ TextObjectEditor::TextObjectEditor(wxWindow* parent,
|
||||
|
||||
// Update from the text object
|
||||
m_textCtrl->SetValue(object.GetString());
|
||||
m_fontTextCtrl->SetValue(object.GetFontFilename());
|
||||
m_fontTextCtrl->SetValue(object.GetFontName());
|
||||
m_sizeCombobox->SetValue(gd::String::From<float>(object.GetCharacterSize()));
|
||||
textColor =
|
||||
wxColour(object.GetColorR(), object.GetColorG(), object.GetColorB());
|
||||
@@ -56,7 +56,7 @@ TextObjectEditor::~TextObjectEditor() {}
|
||||
void TextObjectEditor::OnOkBtClicked(wxCommandEvent& event) {
|
||||
// Update the text object
|
||||
object.SetString(m_textCtrl->GetValue());
|
||||
object.SetFontFilename(m_fontTextCtrl->GetValue());
|
||||
object.SetFontName(m_fontTextCtrl->GetValue());
|
||||
object.SetCharacterSize(gd::String(m_sizeCombobox->GetValue()).To<float>());
|
||||
object.SetColor(textColor.Red(), textColor.Green(), textColor.Blue());
|
||||
object.SetBold(m_toolbar->GetToolToggled(BOLD_TOOL_ID));
|
||||
|
@@ -54,7 +54,7 @@ TextObject::~TextObject(){};
|
||||
void TextObject::DoUnserializeFrom(gd::Project& project,
|
||||
const gd::SerializerElement& element) {
|
||||
SetString(element.GetChild("string", 0, "String").GetValue().GetString());
|
||||
SetFontFilename(element.GetChild("font", 0, "Font").GetValue().GetString());
|
||||
SetFontName(element.GetChild("font", 0, "Font").GetValue().GetString());
|
||||
SetCharacterSize(element.GetChild("characterSize", 0, "CharacterSize")
|
||||
.GetValue()
|
||||
.GetInt());
|
||||
@@ -119,7 +119,7 @@ void TextObject::LoadResources(gd::Project& project, gd::Layout& layout) {
|
||||
|
||||
void TextObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
element.AddChild("string").SetValue(GetString());
|
||||
element.AddChild("font").SetValue(GetFontFilename());
|
||||
element.AddChild("font").SetValue(GetFontName());
|
||||
element.AddChild("characterSize").SetValue(GetCharacterSize());
|
||||
element.AddChild("color")
|
||||
.SetAttribute("r", (int)GetColorR())
|
||||
@@ -133,7 +133,7 @@ void TextObject::DoSerializeTo(gd::SerializerElement& element) const {
|
||||
}
|
||||
|
||||
void TextObject::ExposeResources(gd::ArbitraryResourceWorker& worker) {
|
||||
worker.ExposeFile(fontName);
|
||||
worker.ExposeFont(fontName);
|
||||
}
|
||||
|
||||
bool TextObject::GenerateThumbnail(const gd::Project& project,
|
||||
@@ -157,19 +157,12 @@ void TextObject::EditObject(wxWindow* parent,
|
||||
}
|
||||
#endif
|
||||
|
||||
void TextObject::SetFontFilename(const gd::String& fontFilename) {
|
||||
fontName = fontFilename;
|
||||
#if defined(GD_IDE_ONLY)
|
||||
fontName = gd::AbstractFileSystem::NormalizeSeparator(fontName);
|
||||
#endif
|
||||
};
|
||||
|
||||
/* RuntimeTextObject : */
|
||||
|
||||
RuntimeTextObject::RuntimeTextObject(RuntimeScene& scene,
|
||||
const TextObject& textObject)
|
||||
: RuntimeObject(scene, textObject), opacity(255), angle(0) {
|
||||
ChangeFont(textObject.GetFontFilename());
|
||||
ChangeFont(textObject.GetFontName());
|
||||
SetSmooth(textObject.IsSmoothed());
|
||||
SetColor(
|
||||
textObject.GetColorR(), textObject.GetColorG(), textObject.GetColorB());
|
||||
@@ -332,7 +325,7 @@ void RuntimeTextObject::GetPropertyForDebugger(std::size_t propertyNb,
|
||||
value = GetString();
|
||||
} else if (propertyNb == 1) {
|
||||
name = _("Font");
|
||||
value = GetFontFilename();
|
||||
value = GetFontName();
|
||||
} else if (propertyNb == 2) {
|
||||
name = _("Font Size");
|
||||
value = gd::String::From(GetCharacterSize());
|
||||
|
@@ -15,26 +15,17 @@ This project is released under the MIT License.
|
||||
class ImageManager;
|
||||
class RuntimeScene;
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Object;
|
||||
}
|
||||
namespace gd {
|
||||
class ImageManager;
|
||||
}
|
||||
namespace gd {
|
||||
class InitialInstance;
|
||||
}
|
||||
#if defined(GD_IDE_ONLY)
|
||||
class wxBitmap;
|
||||
namespace gd {
|
||||
class Project;
|
||||
}
|
||||
class wxWindow;
|
||||
namespace gd {
|
||||
class MainFrameWrapper;
|
||||
}
|
||||
namespace gd {
|
||||
class ResourcesMergingHelper;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -82,13 +73,13 @@ class GD_EXTENSION_API TextObject : public gd::Object {
|
||||
*/
|
||||
inline float GetCharacterSize() const { return characterSize; };
|
||||
|
||||
/** \brief Return the font filename.
|
||||
/** \brief Return the name of the font resource used for the text.
|
||||
*/
|
||||
inline const gd::String& GetFontFilename() const { return fontName; };
|
||||
inline const gd::String& GetFontName() const { return fontName; };
|
||||
|
||||
/** \brief Change the font filename.
|
||||
/** \brief Change the font resource used for the text.
|
||||
*/
|
||||
void SetFontFilename(const gd::String& fontFilename);
|
||||
void SetFontName(const gd::String& resourceName) { fontName = resourceName; };
|
||||
|
||||
bool IsBold() const { return bold; };
|
||||
void SetBold(bool enable) { bold = enable; };
|
||||
@@ -170,9 +161,9 @@ class GD_EXTENSION_API RuntimeTextObject : public RuntimeObject {
|
||||
*/
|
||||
void ChangeFont(const gd::String& fontFilename);
|
||||
|
||||
/** \brief Return the font file name.
|
||||
/** \brief Return the font resource name.
|
||||
*/
|
||||
inline gd::String GetFontFilename() const { return fontName; };
|
||||
inline gd::String GetFontName() const { return fontName; };
|
||||
|
||||
void SetFontStyle(int style);
|
||||
int GetFontStyle();
|
||||
|
@@ -1,6 +1,7 @@
|
||||
gdjs.TextRuntimeObjectCocosRenderer = function(runtimeObject, runtimeScene)
|
||||
{
|
||||
this._object = runtimeObject;
|
||||
this._fontManager = runtimeScene.getGame().getFontManager();
|
||||
|
||||
this._text = new cc.LabelTTF(" ", "Arial", 38);
|
||||
this._text.disableStroke();
|
||||
@@ -33,8 +34,8 @@ gdjs.TextRuntimeObjectCocosRenderer.prototype.updateStyle = function() {
|
||||
'Arial' :
|
||||
(
|
||||
gdjs.CocosTools.isHTML5() ?
|
||||
'gdjs_font_' + this._object._fontName :
|
||||
'res/' + this._object._fontName
|
||||
this._fontManager.getFontFamily(this._object._fontName) :
|
||||
'res/' + this._fontManager.getFontFile(this._object._fontName)
|
||||
);
|
||||
this._text.setFontName(fontName);
|
||||
};
|
||||
|
@@ -1,6 +1,7 @@
|
||||
gdjs.TextRuntimeObjectPixiRenderer = function(runtimeObject, runtimeScene)
|
||||
{
|
||||
this._object = runtimeObject;
|
||||
this._fontManager = runtimeScene.getGame().getFontManager();
|
||||
|
||||
if ( this._text === undefined ) this._text = new PIXI.Text(" ", {align:"left"});
|
||||
this._text.anchor.x = 0.5;
|
||||
@@ -28,7 +29,7 @@ gdjs.TextRuntimeObjectPixiRenderer.prototype.ensureUpToDate = function() {
|
||||
};
|
||||
|
||||
gdjs.TextRuntimeObjectPixiRenderer.prototype.updateStyle = function() {
|
||||
var fontName = this._object._fontName ? "\"gdjs_font_" + this._object._fontName + "\"" : 'Arial';
|
||||
var fontName = "\"" + this._fontManager.getFontFamily(this._object._fontName) + "\"";
|
||||
|
||||
var style = this._text.style;
|
||||
style.fontStyle = this._object._italic ? 'italic' : 'normal';
|
||||
|
@@ -172,7 +172,8 @@ bool Exporter::ExportWholePixiProject(
|
||||
else if (exportForFacebookInstantGames)
|
||||
source = gdjsRoot + "/Runtime/FacebookInstantGames/index.html";
|
||||
|
||||
if (!helper.ExportPixiIndexFile(source, exportDir, includesFiles, "")) {
|
||||
if (!helper.ExportPixiIndexFile(
|
||||
exportedProject, source, exportDir, includesFiles, "")) {
|
||||
gd::LogError(_("Error during export:\n") + lastError);
|
||||
return false;
|
||||
}
|
||||
@@ -248,7 +249,8 @@ bool Exporter::ExportWholeCocos2dProject(gd::Project& project,
|
||||
helper.AddLibsInclude(false, true, false, includesFiles);
|
||||
|
||||
// Export events
|
||||
if (!helper.ExportEventsCode(exportedProject, codeOutputDir, includesFiles, false)) {
|
||||
if (!helper.ExportEventsCode(
|
||||
exportedProject, codeOutputDir, includesFiles, false)) {
|
||||
gd::LogError(_("Error during exporting! Unable to export events:\n") +
|
||||
lastError);
|
||||
return false;
|
||||
|
@@ -44,27 +44,56 @@ static void InsertUnique(std::vector<gd::String> &container, gd::String str) {
|
||||
container.push_back(str);
|
||||
}
|
||||
|
||||
static void GenerateFontsDeclaration(gd::AbstractFileSystem &fs,
|
||||
const gd::String &outputDir,
|
||||
gd::String &css,
|
||||
gd::String &html,
|
||||
gd::String urlPrefix = "") {
|
||||
std::vector<gd::String> ttfFiles = fs.ReadDir(outputDir, ".TTF");
|
||||
for (std::size_t i = 0; i < ttfFiles.size(); ++i) {
|
||||
gd::String relativeFile = ttfFiles[i];
|
||||
fs.MakeRelative(relativeFile, outputDir);
|
||||
static void GenerateFontsDeclaration(
|
||||
const gd::ResourcesManager &resourcesManager,
|
||||
gd::AbstractFileSystem &fs,
|
||||
const gd::String &outputDir,
|
||||
gd::String &css,
|
||||
gd::String &html,
|
||||
gd::String urlPrefix = "") {
|
||||
std::set<gd::String> files;
|
||||
auto makeCSSDeclarationFor = [&urlPrefix](gd::String relativeFile) {
|
||||
gd::String css;
|
||||
css += "@font-face{ font-family : \"gdjs_font_";
|
||||
css += relativeFile;
|
||||
css += "\"; src : url('";
|
||||
css += urlPrefix + relativeFile;
|
||||
css += "') format('truetype'); }";
|
||||
css += "') format('truetype'); }\n";
|
||||
|
||||
// Use the font for a dummy text to trigger immediate load of the font at
|
||||
// game startup
|
||||
return css;
|
||||
};
|
||||
|
||||
for (auto &resourceName : resourcesManager.GetAllResourceNames()) {
|
||||
const gd::Resource &resource = resourcesManager.GetResource(resourceName);
|
||||
if (resource.GetKind() != "font") continue;
|
||||
|
||||
gd::String relativeFile = resource.GetFile();
|
||||
css += makeCSSDeclarationFor(relativeFile);
|
||||
files.insert(relativeFile);
|
||||
}
|
||||
|
||||
// Compatibility with GD <= 5.0-beta56
|
||||
// Before, fonts were detected by scanning the export folder for .TTF files.
|
||||
// Text Object (or anything using a font) was just declaring the font filename
|
||||
// as a file (using ArbitraryResourceWorker::ExposeFile) for export.
|
||||
// We still support this, the time everything is migrated to using font
|
||||
// resources.
|
||||
std::vector<gd::String> ttfFiles = fs.ReadDir(outputDir, ".TTF");
|
||||
for (std::size_t i = 0; i < ttfFiles.size(); ++i) {
|
||||
gd::String relativeFile = ttfFiles[i];
|
||||
fs.MakeRelative(relativeFile, outputDir);
|
||||
|
||||
// Skip font files already in resources
|
||||
if (files.find(relativeFile) != files.end()) continue;
|
||||
|
||||
css += makeCSSDeclarationFor(relativeFile);
|
||||
|
||||
// This is needed to trigger the loading of the fonts.
|
||||
html += "<div style=\"font-family: 'gdjs_font_";
|
||||
html += relativeFile;
|
||||
html += "'; color: black;\">.</div>";
|
||||
}
|
||||
// end of compatibility code
|
||||
}
|
||||
|
||||
ExporterHelper::ExporterHelper(gd::AbstractFileSystem &fileSystem,
|
||||
@@ -119,7 +148,8 @@ bool ExporterHelper::ExportLayoutForPixiPreview(gd::Project &project,
|
||||
ExportIncludesAndLibs(includesFiles, exportDir, false);
|
||||
|
||||
// Create the index file
|
||||
if (!ExportPixiIndexFile(gdjsRoot + "/Runtime/index.html",
|
||||
if (!ExportPixiIndexFile(exportedProject,
|
||||
gdjsRoot + "/Runtime/index.html",
|
||||
exportDir,
|
||||
includesFiles,
|
||||
additionalSpec))
|
||||
@@ -148,6 +178,7 @@ gd::String ExporterHelper::ExportToJSON(gd::AbstractFileSystem &fs,
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportPixiIndexFile(
|
||||
const gd::Project &project,
|
||||
gd::String source,
|
||||
gd::String exportDir,
|
||||
const std::vector<gd::String> &includesFiles,
|
||||
@@ -156,8 +187,14 @@ bool ExporterHelper::ExportPixiIndexFile(
|
||||
|
||||
// Generate custom declarations for font resources
|
||||
gd::String customCss;
|
||||
gd::String customHtml;
|
||||
GenerateFontsDeclaration(fs, exportDir, customCss, customHtml);
|
||||
gd::String customHtml; // Custom HTML is only needed for the deprecated way
|
||||
// of loading fonts
|
||||
GenerateFontsDeclaration(project.GetResourcesManager(),
|
||||
fs, // File system is only needed for the deprecated
|
||||
// way of loading fonts
|
||||
exportDir,
|
||||
customCss,
|
||||
customHtml);
|
||||
|
||||
// Generate the file
|
||||
if (!CompleteIndexFile(
|
||||
@@ -229,10 +266,13 @@ bool ExporterHelper::ExportCordovaConfigFile(const gd::Project &project,
|
||||
.FindAndReplace("GDJS_ICON_IOS_100",
|
||||
getIconFilename("ios", "icon-100"));
|
||||
|
||||
if(!project.GetAdMobAppId().empty()){
|
||||
str = str.FindAndReplace("<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->",
|
||||
"<plugin name=\"cordova-plugin-admob-free\" spec=\"~0.21.0\">\n"
|
||||
"\t\t<variable name=\"ADMOB_APP_ID\" value=\"" + project.GetAdMobAppId() + "\" />\n"
|
||||
if (!project.GetAdMobAppId().empty()) {
|
||||
str = str.FindAndReplace(
|
||||
"<!-- GDJS_ADMOB_PLUGIN_AND_APPLICATION_ID -->",
|
||||
"<plugin name=\"cordova-plugin-admob-free\" spec=\"~0.21.0\">\n"
|
||||
"\t\t<variable name=\"ADMOB_APP_ID\" value=\"" +
|
||||
project.GetAdMobAppId() +
|
||||
"\" />\n"
|
||||
"\t</plugin>");
|
||||
}
|
||||
|
||||
@@ -267,8 +307,12 @@ bool ExporterHelper::ExportCocos2dFiles(
|
||||
// Generate custom declarations for font resources
|
||||
gd::String customCss;
|
||||
gd::String customHtml;
|
||||
GenerateFontsDeclaration(
|
||||
fs, exportDir + "/res", customCss, customHtml, "res/");
|
||||
GenerateFontsDeclaration(project.GetResourcesManager(),
|
||||
fs,
|
||||
exportDir + "/res",
|
||||
customCss,
|
||||
customHtml,
|
||||
"res/");
|
||||
|
||||
// Generate the file
|
||||
std::vector<gd::String> noIncludesInThisFile;
|
||||
@@ -476,6 +520,8 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
"pixi-renderers/loadingscreen-pixi-renderer.js");
|
||||
InsertUnique(includesFiles, "howler-sound-manager/howler.min.js");
|
||||
InsertUnique(includesFiles, "howler-sound-manager/howler-sound-manager.js");
|
||||
InsertUnique(includesFiles, "fontfaceobserver-font-manager/fontfaceobserver.js");
|
||||
InsertUnique(includesFiles, "fontfaceobserver-font-manager/fontfaceobserver-font-manager.js");
|
||||
}
|
||||
|
||||
if (cocosRenderers) {
|
||||
@@ -492,6 +538,8 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles,
|
||||
"cocos-renderers/spriteruntimeobject-cocos-renderer.js");
|
||||
InsertUnique(includesFiles, "cocos-sound-manager/cocos-sound-manager.js");
|
||||
InsertUnique(includesFiles, "fontfaceobserver-font-manager/fontfaceobserver.js");
|
||||
InsertUnique(includesFiles, "fontfaceobserver-font-manager/fontfaceobserver-font-manager.js");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -156,7 +156,8 @@ class ExporterHelper {
|
||||
* important. \param additionalSpec JSON string that will be passed to the
|
||||
* gdjs.RuntimeGame object.
|
||||
*/
|
||||
bool ExportPixiIndexFile(gd::String source,
|
||||
bool ExportPixiIndexFile(const gd::Project &project,
|
||||
gd::String source,
|
||||
gd::String exportDir,
|
||||
const std::vector<gd::String> &includesFiles,
|
||||
gd::String additionalSpec = "");
|
||||
|
@@ -17,7 +17,9 @@ gdjs.CocosImageManager = function(resources)
|
||||
|
||||
var that = this;
|
||||
resources.forEach(function(res) {
|
||||
that._resources[res.name] = res;
|
||||
if ( res.file && res.kind === "image" ) {
|
||||
that._resources[res.name] = res;
|
||||
}
|
||||
})
|
||||
|
||||
};
|
||||
@@ -67,6 +69,6 @@ gdjs.CocosImageManager.prototype.loadTextures = function(onProgress, onComplete)
|
||||
});
|
||||
|
||||
cc.LoaderScene.preload(files, function () {
|
||||
onComplete();
|
||||
onComplete(files.length);
|
||||
});
|
||||
}
|
||||
|
@@ -152,5 +152,5 @@ gdjs.CocosSoundManager.prototype.preloadAudio = function(onProgress, onComplete,
|
||||
}
|
||||
|
||||
//TODO: sound preloading
|
||||
onComplete();
|
||||
onComplete(files.length);
|
||||
}
|
||||
|
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-present Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* FontFaceObserverFontManager loads fonts (using fontfaceobserver library)
|
||||
* from the game resources (see `loadFonts`), and allow to access to
|
||||
* the font families of the loaded fonts during the game (see `getFontFamily`).
|
||||
*
|
||||
* @font-face declarations must be have been added separately in the index.html
|
||||
* (or any CSS file).
|
||||
*
|
||||
* @class FontFaceObserverFontManager
|
||||
* @memberof gdjs
|
||||
* @param {Object} resources The resources data of the game.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager = function(resources)
|
||||
{
|
||||
this._resources = resources;
|
||||
this._loadedFontFamily = {}; // Associate font resource names to the loaded font family
|
||||
this._loadedFonts = {}; // Associate font resource names to the resources, for faster access
|
||||
};
|
||||
|
||||
gdjs.FontManager = gdjs.FontFaceObserverFontManager; //Register the class to let the engine use it.
|
||||
|
||||
/**
|
||||
* Return the font family associated to the specified font resource name.
|
||||
* The font resource must have been loaded before. If that's not the case,
|
||||
* a font family will be returned but without guarantee of it being loaded (to
|
||||
* keep compatibility with GDevelop 5.0-beta56 and previous).
|
||||
*
|
||||
* @param {string} resourceName The name of the resource to get.
|
||||
* @returns {string} The font family to be used for this font resource,
|
||||
* or "Arial" if `resourceName` is empty.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager.prototype.getFontFamily = function(resourceName) {
|
||||
if (this._loadedFontFamily[resourceName]) {
|
||||
return this._loadedFontFamily[resourceName];
|
||||
}
|
||||
|
||||
return resourceName ?
|
||||
gdjs.FontFaceObserverFontManager._getFontFamilyFromFilename(resourceName) :
|
||||
'Arial';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the font file associated to the specified font resource name.
|
||||
* The font resource must have been loaded before. If that's not the case,
|
||||
* the resource name will be returned (to
|
||||
* keep compatibility with GDevelop 5.0-beta56 and previous).
|
||||
*
|
||||
* Should only be useful for renderers running on a non HTML5/non browser environment.
|
||||
*
|
||||
* @param {string} resourceName The name of the resource to get.
|
||||
* @returns {string} The file of the font resource.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager.prototype.getFontFile = function(resourceName) {
|
||||
if (this._loadedFonts[resourceName]) {
|
||||
return this._loadedFonts[resourceName].file || '';
|
||||
}
|
||||
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the font family for a given filename.
|
||||
* Should be kept in sync with the declaration of "@font-face" during exports.
|
||||
*
|
||||
* @private
|
||||
* @param {string} filename The filename of the font
|
||||
* @returns {string} The font family to be used for this font resource.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager._getFontFamilyFromFilename = function(filename) {
|
||||
return "gdjs_font_" + filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the specified resources, so that fonts are loaded and can then be
|
||||
* used by using the font family returned by getFontFamily.
|
||||
* @param onProgress Callback called each time a new file is loaded.
|
||||
* @param onComplete Callback called when loading is done.
|
||||
* @param resources The resources to be loaded. If not specified, will load the resources
|
||||
* specified in the FontFaceObserverFontManager constructor.
|
||||
*/
|
||||
gdjs.FontFaceObserverFontManager.prototype.loadFonts = function(onProgress, onComplete, resources) {
|
||||
resources = resources || this._resources;
|
||||
|
||||
//Construct the list of files to be loaded.
|
||||
//For one loaded file, it can have one or more resources
|
||||
//that use it.
|
||||
var filesResources = {};
|
||||
for(var i = 0, len = resources.length;i<len;++i) {
|
||||
var res = resources[i];
|
||||
if ( res.file && res.kind === "font" ) {
|
||||
filesResources[res.file] = filesResources[res.file] ? filesResources[res.file].concat(res) : [res];
|
||||
}
|
||||
}
|
||||
|
||||
var totalCount = Object.keys(filesResources).length;
|
||||
if (totalCount === 0)
|
||||
return onComplete(totalCount); //Nothing to load.
|
||||
|
||||
var loadingCount = 0;
|
||||
var that = this;
|
||||
var onFontLoaded = function(fontFamily, resources) {
|
||||
resources.forEach(function(resource) {
|
||||
that._loadedFontFamily[resource.name] = fontFamily;
|
||||
that._loadedFonts[resource.name] = resource;
|
||||
});
|
||||
|
||||
loadingCount++;
|
||||
onProgress(loadingCount, totalCount);
|
||||
if (loadingCount === totalCount) onComplete(totalCount);
|
||||
}
|
||||
|
||||
Object.keys(filesResources).forEach(function(file) {
|
||||
var fontFamily = gdjs.FontFaceObserverFontManager._getFontFamilyFromFilename(file);
|
||||
var resources = filesResources[file];
|
||||
new FontFaceObserver(fontFamily).load().then(function() {
|
||||
onFontLoaded(fontFamily, resources);
|
||||
}, function() {
|
||||
console.error("Error loading font resource \"" + resources[0].name + "\" (file: " + file + ")");
|
||||
onFontLoaded(fontFamily, resources);
|
||||
});
|
||||
});
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
(function() {
|
||||
var module; //Define an undefined module variable to avoid fontfaceobserver thinking it's used in an environment using require.
|
||||
|
||||
/* Font Face Observer v2.0.13 - © Bram Stein. License: BSD-3-Clause */(function(){'use strict';var f,g=[];function l(a){g.push(a);1==g.length&&f()}function m(){for(;g.length;)g[0](),g.shift()}f=function(){setTimeout(m)};function n(a){this.a=p;this.b=void 0;this.f=[];var b=this;try{a(function(a){q(b,a)},function(a){r(b,a)})}catch(c){r(b,c)}}var p=2;function t(a){return new n(function(b,c){c(a)})}function u(a){return new n(function(b){b(a)})}function q(a,b){if(a.a==p){if(b==a)throw new TypeError;var c=!1;try{var d=b&&b.then;if(null!=b&&"object"==typeof b&&"function"==typeof d){d.call(b,function(b){c||q(a,b);c=!0},function(b){c||r(a,b);c=!0});return}}catch(e){c||r(a,e);return}a.a=0;a.b=b;v(a)}}
|
||||
function r(a,b){if(a.a==p){if(b==a)throw new TypeError;a.a=1;a.b=b;v(a)}}function v(a){l(function(){if(a.a!=p)for(;a.f.length;){var b=a.f.shift(),c=b[0],d=b[1],e=b[2],b=b[3];try{0==a.a?"function"==typeof c?e(c.call(void 0,a.b)):e(a.b):1==a.a&&("function"==typeof d?e(d.call(void 0,a.b)):b(a.b))}catch(h){b(h)}}})}n.prototype.g=function(a){return this.c(void 0,a)};n.prototype.c=function(a,b){var c=this;return new n(function(d,e){c.f.push([a,b,d,e]);v(c)})};
|
||||
function w(a){return new n(function(b,c){function d(c){return function(d){h[c]=d;e+=1;e==a.length&&b(h)}}var e=0,h=[];0==a.length&&b(h);for(var k=0;k<a.length;k+=1)u(a[k]).c(d(k),c)})}function x(a){return new n(function(b,c){for(var d=0;d<a.length;d+=1)u(a[d]).c(b,c)})};window.Promise||(window.Promise=n,window.Promise.resolve=u,window.Promise.reject=t,window.Promise.race=x,window.Promise.all=w,window.Promise.prototype.then=n.prototype.c,window.Promise.prototype["catch"]=n.prototype.g);}());
|
||||
|
||||
(function(){function l(a,b){document.addEventListener?a.addEventListener("scroll",b,!1):a.attachEvent("scroll",b)}function m(a){document.body?a():document.addEventListener?document.addEventListener("DOMContentLoaded",function c(){document.removeEventListener("DOMContentLoaded",c);a()}):document.attachEvent("onreadystatechange",function k(){if("interactive"==document.readyState||"complete"==document.readyState)document.detachEvent("onreadystatechange",k),a()})};function r(a){this.a=document.createElement("div");this.a.setAttribute("aria-hidden","true");this.a.appendChild(document.createTextNode(a));this.b=document.createElement("span");this.c=document.createElement("span");this.h=document.createElement("span");this.f=document.createElement("span");this.g=-1;this.b.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.c.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";
|
||||
this.f.style.cssText="max-width:none;display:inline-block;position:absolute;height:100%;width:100%;overflow:scroll;font-size:16px;";this.h.style.cssText="display:inline-block;width:200%;height:200%;font-size:16px;max-width:none;";this.b.appendChild(this.h);this.c.appendChild(this.f);this.a.appendChild(this.b);this.a.appendChild(this.c)}
|
||||
function t(a,b){a.a.style.cssText="max-width:none;min-width:20px;min-height:20px;display:inline-block;overflow:hidden;position:absolute;width:auto;margin:0;padding:0;top:-999px;white-space:nowrap;font-synthesis:none;font:"+b+";"}function y(a){var b=a.a.offsetWidth,c=b+100;a.f.style.width=c+"px";a.c.scrollLeft=c;a.b.scrollLeft=a.b.scrollWidth+100;return a.g!==b?(a.g=b,!0):!1}function z(a,b){function c(){var a=k;y(a)&&a.a.parentNode&&b(a.g)}var k=a;l(a.b,c);l(a.c,c);y(a)};function A(a,b){var c=b||{};this.family=a;this.style=c.style||"normal";this.weight=c.weight||"normal";this.stretch=c.stretch||"normal"}var B=null,C=null,E=null,F=null;function G(){if(null===C)if(J()&&/Apple/.test(window.navigator.vendor)){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))(?:\.([0-9]+))/.exec(window.navigator.userAgent);C=!!a&&603>parseInt(a[1],10)}else C=!1;return C}function J(){null===F&&(F=!!document.fonts);return F}
|
||||
function K(){if(null===E){var a=document.createElement("div");try{a.style.font="condensed 100px sans-serif"}catch(b){}E=""!==a.style.font}return E}function L(a,b){return[a.style,a.weight,K()?a.stretch:"","100px",b].join(" ")}
|
||||
A.prototype.load=function(a,b){var c=this,k=a||"BESbswy",q=0,D=b||3E3,H=(new Date).getTime();return new Promise(function(a,b){if(J()&&!G()){var M=new Promise(function(a,b){function e(){(new Date).getTime()-H>=D?b():document.fonts.load(L(c,'"'+c.family+'"'),k).then(function(c){1<=c.length?a():setTimeout(e,25)},function(){b()})}e()}),N=new Promise(function(a,c){q=setTimeout(c,D)});Promise.race([N,M]).then(function(){clearTimeout(q);a(c)},function(){b(c)})}else m(function(){function u(){var b;if(b=-1!=
|
||||
f&&-1!=g||-1!=f&&-1!=h||-1!=g&&-1!=h)(b=f!=g&&f!=h&&g!=h)||(null===B&&(b=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent),B=!!b&&(536>parseInt(b[1],10)||536===parseInt(b[1],10)&&11>=parseInt(b[2],10))),b=B&&(f==v&&g==v&&h==v||f==w&&g==w&&h==w||f==x&&g==x&&h==x)),b=!b;b&&(d.parentNode&&d.parentNode.removeChild(d),clearTimeout(q),a(c))}function I(){if((new Date).getTime()-H>=D)d.parentNode&&d.parentNode.removeChild(d),b(c);else{var a=document.hidden;if(!0===a||void 0===a)f=e.a.offsetWidth,
|
||||
g=n.a.offsetWidth,h=p.a.offsetWidth,u();q=setTimeout(I,50)}}var e=new r(k),n=new r(k),p=new r(k),f=-1,g=-1,h=-1,v=-1,w=-1,x=-1,d=document.createElement("div");d.dir="ltr";t(e,L(c,"sans-serif"));t(n,L(c,"serif"));t(p,L(c,"monospace"));d.appendChild(e.a);d.appendChild(n.a);d.appendChild(p.a);document.body.appendChild(d);v=e.a.offsetWidth;w=n.a.offsetWidth;x=p.a.offsetWidth;I();z(e,function(a){f=a;u()});t(e,L(c,'"'+c.family+'",sans-serif'));z(n,function(a){g=a;u()});t(n,L(c,'"'+c.family+'",serif'));
|
||||
z(p,function(a){h=a;u()});t(p,L(c,'"'+c.family+'",monospace'))})})};"object"===typeof module?module.exports=A:(window.FontFaceObserver=A,window.FontFaceObserver.prototype.load=A.prototype.load);}());
|
||||
})()
|
@@ -308,6 +308,10 @@ gdjs.HowlerSoundManager.prototype.clearAll = function() {
|
||||
|
||||
gdjs.HowlerSoundManager.prototype.preloadAudio = function(onProgress, onComplete, resources) {
|
||||
resources = resources || this._resources;
|
||||
|
||||
//Construct the list of files to be loaded.
|
||||
//For one loaded file, it can have one or more resources
|
||||
//that use it.
|
||||
var files = [];
|
||||
for(var i = 0, len = resources.length;i<len;++i) {
|
||||
var res = resources[i];
|
||||
@@ -320,13 +324,13 @@ gdjs.HowlerSoundManager.prototype.preloadAudio = function(onProgress, onComplete
|
||||
}
|
||||
}
|
||||
|
||||
if (files.length === 0) return onComplete();
|
||||
if (files.length === 0) return onComplete(files.length);
|
||||
|
||||
var loaded = 0;
|
||||
function onLoad(audioFile) {
|
||||
loaded++;
|
||||
if (loaded === files.length) {
|
||||
return onComplete();
|
||||
return onComplete(files.length);
|
||||
}
|
||||
|
||||
onProgress(loaded, files.length);
|
||||
|
@@ -96,7 +96,7 @@ gdjs.PixiImageManager.prototype.loadTextures = function(onProgress, onComplete,
|
||||
|
||||
var totalCount = Object.keys(files).length;
|
||||
if (totalCount === 0)
|
||||
return onComplete(); //Nothing to load.
|
||||
return onComplete(totalCount); //Nothing to load.
|
||||
|
||||
var loadingCount = 0;
|
||||
var loader = PIXI.loader;
|
||||
@@ -117,7 +117,7 @@ gdjs.PixiImageManager.prototype.loadTextures = function(onProgress, onComplete,
|
||||
}
|
||||
}
|
||||
|
||||
onComplete();
|
||||
onComplete(totalCount);
|
||||
});
|
||||
loader.on('progress', function() {
|
||||
loadingCount++;
|
||||
|
@@ -23,6 +23,9 @@ gdjs.RuntimeGame = function(data, spec) {
|
||||
this._soundManager = new gdjs.SoundManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._fontManager = new gdjs.FontManager(
|
||||
data.resources ? data.resources.resources : undefined
|
||||
);
|
||||
this._minFPS = data ? parseInt(data.properties.minFPS, 10) : 15;
|
||||
|
||||
this._defaultWidth = data.properties.windowWidth; //Default size for scenes cameras
|
||||
@@ -81,6 +84,14 @@ gdjs.RuntimeGame.prototype.getImageManager = function() {
|
||||
return this._imageManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the gdjs.FontManager of the RuntimeGame.
|
||||
* @return {gdjs.FontManager} The font manager.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.getFontManager = function() {
|
||||
return this._fontManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the input manager of the game, storing mouse, keyboard
|
||||
* and touches states.
|
||||
@@ -238,12 +249,15 @@ gdjs.RuntimeGame.prototype.getMinimalFramerate = function() {
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.pause = function(enable) {
|
||||
this._paused = enable;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load all assets, displaying progress in renderer.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.loadAllAssets = function(callback, progressCallback) {
|
||||
gdjs.RuntimeGame.prototype.loadAllAssets = function(
|
||||
callback,
|
||||
progressCallback
|
||||
) {
|
||||
var loadingScreen = new gdjs.LoadingScreenRenderer(
|
||||
this.getRenderer(),
|
||||
this._data.properties.loadingScreen
|
||||
@@ -253,20 +267,33 @@ gdjs.RuntimeGame.prototype.loadAllAssets = function(callback, progressCallback)
|
||||
var that = this;
|
||||
this._imageManager.loadTextures(
|
||||
function(count, total) {
|
||||
var percent = Math.floor(count / allAssetsTotal * 100);
|
||||
var percent = Math.floor((count / allAssetsTotal) * 100);
|
||||
loadingScreen.render(percent);
|
||||
if (progressCallback) progressCallback(percent);
|
||||
},
|
||||
function() {
|
||||
function(texturesTotalCount) {
|
||||
that._soundManager.preloadAudio(
|
||||
function(count, total) {
|
||||
loadingScreen.render(
|
||||
Math.floor((allAssetsTotal - total + count) / allAssetsTotal * 100)
|
||||
Math.floor(((texturesTotalCount + count) / allAssetsTotal) * 100)
|
||||
);
|
||||
},
|
||||
function() {
|
||||
loadingScreen.unload();
|
||||
callback();
|
||||
function(audioTotalCount) {
|
||||
that._fontManager.loadFonts(
|
||||
function(count, total) {
|
||||
loadingScreen.render(
|
||||
Math.floor(
|
||||
((texturesTotalCount + audioTotalCount + count) /
|
||||
allAssetsTotal) *
|
||||
100
|
||||
)
|
||||
);
|
||||
},
|
||||
function() {
|
||||
loadingScreen.unload();
|
||||
callback();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -329,10 +356,15 @@ gdjs.RuntimeGame.prototype.startGameLoop = function() {
|
||||
* @param {?string} mode `adaptWidth` to change the width, `adaptHeight` to change the height. If not defined, will use the game "sizeOnStartupMode" .
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.adaptRendererSizeToFillScreen = function(mode) {
|
||||
if (!gdjs.RuntimeGameRenderer || !gdjs.RuntimeGameRenderer.getScreenWidth || !gdjs.RuntimeGameRenderer.getScreenHeight)
|
||||
if (
|
||||
!gdjs.RuntimeGameRenderer ||
|
||||
!gdjs.RuntimeGameRenderer.getScreenWidth ||
|
||||
!gdjs.RuntimeGameRenderer.getScreenHeight
|
||||
)
|
||||
return;
|
||||
|
||||
newMode = mode !== undefined ? mode : (this._data.properties.sizeOnStartupMode || '');
|
||||
newMode =
|
||||
mode !== undefined ? mode : this._data.properties.sizeOnStartupMode || '';
|
||||
|
||||
var screenWidth = gdjs.RuntimeGameRenderer.getScreenWidth();
|
||||
var screenHeight = gdjs.RuntimeGameRenderer.getScreenHeight();
|
||||
@@ -341,10 +373,10 @@ gdjs.RuntimeGame.prototype.adaptRendererSizeToFillScreen = function(mode) {
|
||||
var renderer = this.getRenderer();
|
||||
var width = renderer.getCurrentWidth();
|
||||
var height = renderer.getCurrentHeight();
|
||||
if (newMode === "adaptWidth") {
|
||||
width = height * screenWidth / screenHeight;
|
||||
} else if (newMode === "adaptHeight") {
|
||||
height = width * screenHeight / screenWidth;
|
||||
if (newMode === 'adaptWidth') {
|
||||
width = (height * screenWidth) / screenHeight;
|
||||
} else if (newMode === 'adaptHeight') {
|
||||
height = (width * screenHeight) / screenWidth;
|
||||
}
|
||||
|
||||
// Update the renderer size, and also the default size of the game so that
|
||||
@@ -352,19 +384,21 @@ gdjs.RuntimeGame.prototype.adaptRendererSizeToFillScreen = function(mode) {
|
||||
renderer.setSize(width, height);
|
||||
this.setDefaultWidth(width);
|
||||
this.setDefaultHeight(height);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a profiler for the currently running scene.
|
||||
* @param {Function} onProfilerStopped Function to be called when the profiler is stopped. Will be passed the profiler as argument.
|
||||
*/
|
||||
gdjs.RuntimeGame.prototype.startCurrentSceneProfiler = function(onProfilerStopped) {
|
||||
gdjs.RuntimeGame.prototype.startCurrentSceneProfiler = function(
|
||||
onProfilerStopped
|
||||
) {
|
||||
var currentScene = this._sceneStack.getCurrentScene();
|
||||
if (!currentScene) return false;
|
||||
|
||||
currentScene.startProfiler(onProfilerStopped);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the profiler for the currently running scene.
|
||||
@@ -374,4 +408,4 @@ gdjs.RuntimeGame.prototype.stopCurrentSceneProfiler = function() {
|
||||
if (!currentScene) return null;
|
||||
|
||||
currentScene.stopProfiler();
|
||||
}
|
||||
};
|
||||
|
@@ -15,6 +15,8 @@ module.exports = function(config) {
|
||||
'../Runtime/pixi-renderers/*.js',
|
||||
'../Runtime/howler-sound-manager/howler.min.js',
|
||||
'../Runtime/howler-sound-manager/howler-sound-manager.js',
|
||||
'../Runtime/fontfaceobserver-font-manager/fontfaceobserver.js',
|
||||
'../Runtime/fontfaceobserver-font-manager/fontfaceobserver-font-manager.js',
|
||||
'../Runtime/timemanager.js',
|
||||
'../Runtime/runtimeobject.js',
|
||||
'../Runtime/runtimescene.js',
|
||||
|
@@ -35,6 +35,7 @@ export default class PanelSpriteEditor extends React.Component<
|
||||
panelSpriteObject.setTexture(resourceName);
|
||||
this.forceUpdate();
|
||||
}}
|
||||
floatingLabelText="Select an image"
|
||||
/>
|
||||
</Line>
|
||||
<Line>
|
||||
|
@@ -116,6 +116,7 @@ export default class ParticleEmitterEditor extends React.Component<
|
||||
particleEmitterObject.setParticleTexture(resourceName);
|
||||
this.forceUpdate();
|
||||
}}
|
||||
floatingLabelText="Select an image"
|
||||
/>
|
||||
</Line>
|
||||
<Line>
|
||||
|
@@ -5,6 +5,8 @@ import TextField from 'material-ui/TextField';
|
||||
import { Line, Column } from '../../UI/Grid';
|
||||
import ColorPicker from '../../UI/ColorField/ColorPicker';
|
||||
import MiniToolbar, { MiniToolbarText } from '../../UI/MiniToolbar';
|
||||
import ResourceSelector from '../../ResourcesList/ResourceSelector';
|
||||
import ResourcesLoader from '../../ResourcesLoader';
|
||||
import { type EditorProps } from './EditorProps.flow';
|
||||
const gd = global.gd;
|
||||
|
||||
@@ -26,7 +28,13 @@ const styles = {
|
||||
|
||||
export default class TextEditor extends React.Component<EditorProps, void> {
|
||||
render() {
|
||||
const { object } = this.props;
|
||||
const {
|
||||
object,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
} = this.props;
|
||||
const textObject = gd.asTextObject(object);
|
||||
|
||||
return (
|
||||
@@ -75,14 +83,20 @@ export default class TextEditor extends React.Component<EditorProps, void> {
|
||||
}}
|
||||
style={styles.checkbox}
|
||||
/>
|
||||
<TextField
|
||||
hintText="Font"
|
||||
<ResourceSelector
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="font"
|
||||
fullWidth
|
||||
value={textObject.getFontFilename()}
|
||||
onChange={(e, value) => {
|
||||
textObject.setFontFilename(value);
|
||||
initialResourceName={textObject.getFontName()}
|
||||
onChange={resourceName => {
|
||||
textObject.setFontName(resourceName);
|
||||
this.forceUpdate();
|
||||
}}
|
||||
hintText="Choose a font"
|
||||
/>
|
||||
</MiniToolbar>
|
||||
<Line noMargin>
|
||||
|
@@ -34,6 +34,7 @@ export default class TiledSpriteEditor extends React.Component<
|
||||
tiledSpriteObject.setTexture(resourceName);
|
||||
this.forceUpdate();
|
||||
}}
|
||||
floatingLabelText="Select an image"
|
||||
/>
|
||||
</Line>
|
||||
<Line>
|
||||
|
@@ -119,37 +119,58 @@ export default class PixiResourcesLoader {
|
||||
*/
|
||||
static loadFontFamily(
|
||||
project: gdProject,
|
||||
fontFilename: string
|
||||
resourceName: string
|
||||
): Promise<string> {
|
||||
// Avoid reloading a font if it's already cached
|
||||
if (loadedFontFamilies[fontFilename]) {
|
||||
return Promise.resolve(loadedFontFamilies[fontFilename]);
|
||||
if (loadedFontFamilies[resourceName]) {
|
||||
return Promise.resolve(loadedFontFamilies[resourceName]);
|
||||
}
|
||||
|
||||
const fontFamily = slugs(resourceName);
|
||||
let fullFilename = null;
|
||||
if (project.getResourcesManager().hasResource(resourceName)) {
|
||||
const resource = project.getResourcesManager().getResource(resourceName);
|
||||
if (resource.getKind() === 'font') {
|
||||
fullFilename = ResourcesLoader.getResourceFullUrl(
|
||||
project,
|
||||
resourceName
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Compatibility with GD <= 5.0-beta56
|
||||
// Assume resourceName is just the filename to the font
|
||||
fullFilename = ResourcesLoader.getFullUrl(project, resourceName);
|
||||
// end of compatibility code
|
||||
}
|
||||
|
||||
if (!fullFilename) {
|
||||
// If no resource is found/resource is not a font, default to Arial,
|
||||
// as done by the game engine too.
|
||||
return Promise.resolve('Arial');
|
||||
}
|
||||
|
||||
const fontFamily = slugs(fontFilename);
|
||||
const fullFilename = ResourcesLoader.getFullUrl(project, fontFilename);
|
||||
return loadFontFace(
|
||||
fontFamily,
|
||||
`url("${fullFilename}")`,
|
||||
{}
|
||||
).then(loadedFace => {
|
||||
loadedFontFamilies[fontFilename] = fontFamily;
|
||||
loadedFontFamilies[resourceName] = fontFamily;
|
||||
|
||||
return fontFamily;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the font family name for the given font from its url/filename.
|
||||
* Get the font family name for the given font resource.
|
||||
* The font won't be loaded.
|
||||
* @returns The font-family to be used to render a text with the font.
|
||||
*/
|
||||
static getFontFamily(project: gdProject, fontFilename: string) {
|
||||
if (loadedFontFamilies[fontFilename]) {
|
||||
return loadedFontFamilies[fontFilename];
|
||||
static getFontFamily(project: gdProject, resourceName: string) {
|
||||
if (loadedFontFamilies[resourceName]) {
|
||||
return loadedFontFamilies[resourceName];
|
||||
}
|
||||
|
||||
const fontFamily = slugs(fontFilename);
|
||||
const fontFamily = slugs(resourceName);
|
||||
return fontFamily;
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,7 @@ function RenderedTextInstance(
|
||||
this._styleFontDirty = true;
|
||||
this._fontFamily = this._pixiResourcesLoader.getFontFamily(
|
||||
this._project,
|
||||
textObject.getFontFilename()
|
||||
textObject.getFontName()
|
||||
);
|
||||
this.update();
|
||||
}
|
||||
@@ -69,11 +69,11 @@ RenderedTextInstance.prototype.update = function() {
|
||||
this._styleFontDirty = true;
|
||||
}
|
||||
|
||||
if (this._fontFilename !== textObject.getFontFilename()) {
|
||||
if (this._fontName !== textObject.getFontName()) {
|
||||
//Avoid calling loadFontFamily if the font didn't changed.
|
||||
this._fontFilename = textObject.getFontFilename();
|
||||
this._fontName = textObject.getFontName();
|
||||
this._pixiResourcesLoader
|
||||
.loadFontFamily(this._project, textObject.getFontFilename())
|
||||
.loadFontFamily(this._project, textObject.getFontName())
|
||||
.then(fontFamily => {
|
||||
// Once the font is loaded, we can use the given fontFamily.
|
||||
this._fontFamily = fontFamily;
|
||||
|
@@ -86,6 +86,14 @@ const publicAudioUrls = [
|
||||
'https://df5lqcdudryde.cloudfront.net/examples/space-shooter/sfx_lose.ogg',
|
||||
];
|
||||
|
||||
const publicFontUrls = [
|
||||
// Platformer fonts (see platformer.json in fixtures)
|
||||
'https://df5lqcdudryde.cloudfront.net/examples/platformer/Bimbo_JVE.ttf',
|
||||
|
||||
// Space shooter fonts (see space-shooter.json in fixtures)
|
||||
'https://df5lqcdudryde.cloudfront.net/examples/space-shooter/kenvector_future.ttf',
|
||||
];
|
||||
|
||||
const nameFromUrl = (url: string): string => {
|
||||
const urlParts = url.split('/');
|
||||
return urlParts[urlParts.length - 1]
|
||||
@@ -225,7 +233,7 @@ export default [
|
||||
name: 'publicImageUrlChooser',
|
||||
displayName: 'Choose an image from library',
|
||||
kind: 'image',
|
||||
component: class AudioResourceChooser extends React.Component {
|
||||
component: class ImageResourceChooser extends React.Component {
|
||||
chooseResources = () => {
|
||||
if (this._chooser) return this._chooser.chooseResources();
|
||||
};
|
||||
@@ -243,4 +251,26 @@ export default [
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'publicFontUrlChooser',
|
||||
displayName: 'Choose a font from library',
|
||||
kind: 'font',
|
||||
component: class FontResourceChooser extends React.Component {
|
||||
chooseResources = () => {
|
||||
if (this._chooser) return this._chooser.chooseResources();
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<GenericResourcesChooser
|
||||
{...this.props}
|
||||
urls={publicFontUrls}
|
||||
urlsAreImages={false}
|
||||
createNewResource={() => new gd.FontResource()}
|
||||
title="Choose a font from the library"
|
||||
ref={chooser => (this._chooser = chooser)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@@ -95,6 +95,53 @@ export default [
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'localFontFileOpener',
|
||||
displayName: 'Choose a new font file',
|
||||
kind: 'font',
|
||||
component: class LocalFontFileOpener extends Component {
|
||||
chooseResources = (
|
||||
project,
|
||||
multiSelections = true
|
||||
): Promise<Array<any>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!dialog) return reject('Not supported');
|
||||
|
||||
const properties = ['openFile'];
|
||||
if (multiSelections) properties.push('multiSelections');
|
||||
const projectPath = path.dirname(project.getProjectFile());
|
||||
|
||||
const browserWindow = electron.remote.getCurrentWindow();
|
||||
dialog.showOpenDialog(
|
||||
browserWindow,
|
||||
{
|
||||
title: 'Choose a font file',
|
||||
properties,
|
||||
filters: [{ name: 'Font files', extensions: ['ttf'] }],
|
||||
defaultPath: projectPath,
|
||||
},
|
||||
paths => {
|
||||
if (!paths) return resolve([]);
|
||||
|
||||
const resources = paths.map(resourcePath => {
|
||||
const fontResource = new gd.FontResource();
|
||||
const projectPath = path.dirname(project.getProjectFile());
|
||||
fontResource.setFile(path.relative(projectPath, resourcePath));
|
||||
fontResource.setName(path.relative(projectPath, resourcePath));
|
||||
|
||||
return fontResource;
|
||||
});
|
||||
return resolve(resources);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ type Props = {|
|
||||
initialResourceName: string,
|
||||
onChange: string => void,
|
||||
floatingLabelText?: string,
|
||||
hintText?: string,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
@@ -236,7 +237,8 @@ export default class ResourceSelector extends React.Component<Props, State> {
|
||||
<div style={styles.container}>
|
||||
<AutoComplete
|
||||
{...defaultAutocompleteProps}
|
||||
floatingLabelText={this.props.floatingLabelText || 'Select an image'}
|
||||
floatingLabelText={this.props.floatingLabelText}
|
||||
hintText={this.props.hintText}
|
||||
openOnFocus
|
||||
dataSource={this.autoCompleteData || []}
|
||||
onUpdateInput={this._onUpdate}
|
||||
|
@@ -19,6 +19,7 @@ type Props = {|
|
||||
resourceName: string,
|
||||
onChange: (string) => void,
|
||||
floatingLabelText?: string,
|
||||
hintText?: string,
|
||||
|};
|
||||
|
||||
const ResourceSelectorWithThumbnail = ({
|
||||
@@ -30,6 +31,7 @@ const ResourceSelectorWithThumbnail = ({
|
||||
resourceName,
|
||||
onChange,
|
||||
floatingLabelText,
|
||||
hintText,
|
||||
}: Props) => {
|
||||
return (
|
||||
<div style={{ flex: 1, display: 'flex', alignItems: 'flex-end' }}>
|
||||
@@ -45,6 +47,7 @@ const ResourceSelectorWithThumbnail = ({
|
||||
initialResourceName={resourceName}
|
||||
onChange={onChange}
|
||||
floatingLabelText={floatingLabelText}
|
||||
hintText={hintText}
|
||||
/>
|
||||
</div>
|
||||
<ResourceThumbnail
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
|
||||
export type ResourceKind = 'image' | 'audio';
|
||||
export type ResourceKind = 'image' | 'audio' | 'font';
|
||||
|
||||
export type ResourceSource = {
|
||||
name: string,
|
||||
|
@@ -109,12 +109,12 @@ export default class ResourcesList extends React.Component<Props, State> {
|
||||
_removeAllUnusedImages = () => {
|
||||
const { project } = this.props;
|
||||
gd.ProjectResourcesAdder
|
||||
.getAllUselessImages(project)
|
||||
.getAllUseless(project, 'image')
|
||||
.toJSArray()
|
||||
.forEach(imageName => {
|
||||
console.info(`Removing unused image resource: ${imageName}`);
|
||||
});
|
||||
gd.ProjectResourcesAdder.removeAllUselessImages(project);
|
||||
gd.ProjectResourcesAdder.removeAllUseless(project, 'image');
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
|
@@ -804,10 +804,10 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
reloadResourcesFor = (object: gdObject) => {
|
||||
const { project } = this.props;
|
||||
|
||||
const imagesUsedInventorizer = new gd.ImagesUsedInventorizer();
|
||||
object.exposeResources(imagesUsedInventorizer);
|
||||
const objectResourceNames = imagesUsedInventorizer
|
||||
.getAllUsedImages()
|
||||
const resourcesInUse = new gd.ResourcesInUseHelper();
|
||||
object.exposeResources(resourcesInUse);
|
||||
const objectResourceNames = resourcesInUse
|
||||
.getAllImages()
|
||||
.toNewVectorString()
|
||||
.toJSArray();
|
||||
|
||||
|
Reference in New Issue
Block a user