Update asset library editor plugin - new functionality and fixes

This commit is contained in:
Bojidar Marinov 2016-06-07 21:29:15 +03:00
parent ce328ce99b
commit e4a24812cd
No known key found for this signature in database
GPG Key ID: 4D546A8F1E091856
4 changed files with 437 additions and 232 deletions

View File

@ -499,6 +499,7 @@ HTTPRequest::HTTPRequest()
use_ssl=false; use_ssl=false;
response_code=0; response_code=0;
request_sent=false; request_sent=false;
requesting=false;
client.instance(); client.instance();
use_threads=false; use_threads=false;
body_size_limit=-1; body_size_limit=-1;

View File

@ -1,4 +1,32 @@
#include "addon_editor_plugin.h" /*************************************************************************/
/* asset_library_editor_plugin.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "asset_library_editor_plugin.h"
#include "editor_node.h" #include "editor_node.h"
#include "editor_settings.h" #include "editor_settings.h"
@ -16,10 +44,10 @@ void EditorAssetLibraryItem::configure(const String& p_title,int p_asset_id,cons
price->set_text(p_cost); price->set_text(p_cost);
for(int i=0;i<5;i++) { for(int i=0;i<5;i++) {
if (i>2) if (i<p_rating)
stars[i]->set_texture(get_icon("RatingNoStar","EditorIcons"));
else
stars[i]->set_texture(get_icon("RatingStar","EditorIcons")); stars[i]->set_texture(get_icon("RatingStar","EditorIcons"));
else
stars[i]->set_texture(get_icon("RatingNoStar","EditorIcons"));
} }
@ -149,6 +177,7 @@ void EditorAddonLibraryItemDescription::set_image(int p_type,int p_index,const R
for(int i=0;i<preview_images.size();i++) { for(int i=0;i<preview_images.size();i++) {
if (preview_images[i].id==p_index) { if (preview_images[i].id==p_index) {
preview_images[i].button->set_icon(p_image); preview_images[i].button->set_icon(p_image);
break;
} }
} }
//item->call("set_image",p_type,p_index,p_image); //item->call("set_image",p_type,p_index,p_image);
@ -156,8 +185,12 @@ void EditorAddonLibraryItemDescription::set_image(int p_type,int p_index,const R
case EditorAddonLibrary::IMAGE_QUEUE_SCREENSHOT: { case EditorAddonLibrary::IMAGE_QUEUE_SCREENSHOT: {
for(int i=0;i<preview_images.size();i++) { for(int i=0;i<preview_images.size();i++) {
if (preview_images[i].id==p_index && preview_images[i].button->is_pressed()) { if (preview_images[i].id==p_index) {
preview->set_texture(p_image); preview_images[i].image = p_image;
if(preview_images[i].button->is_pressed()) {
_preview_click(p_index);
}
break;
} }
} }
//item->call("set_image",p_type,p_index,p_image); //item->call("set_image",p_type,p_index,p_image);
@ -168,29 +201,46 @@ void EditorAddonLibraryItemDescription::set_image(int p_type,int p_index,const R
void EditorAddonLibraryItemDescription::_bind_methods() { void EditorAddonLibraryItemDescription::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_image"),&EditorAddonLibraryItemDescription::set_image); ObjectTypeDB::bind_method(_MD("set_image"),&EditorAddonLibraryItemDescription::set_image);
ObjectTypeDB::bind_method(_MD("_link_click"),&EditorAddonLibraryItemDescription::_link_click); ObjectTypeDB::bind_method(_MD("_link_click"),&EditorAddonLibraryItemDescription::_link_click);
ObjectTypeDB::bind_method(_MD("_preview_click"),&EditorAddonLibraryItemDescription::_preview_click);
} }
void EditorAddonLibraryItemDescription::_link_click(const String& p_url) { void EditorAddonLibraryItemDescription::_link_click(const String& p_url) {
ERR_FAIL_COND(!p_url.begins_with("http")); ERR_FAIL_COND(!p_url.begins_with("http"));
OS::get_singleton()->shell_open(p_url); OS::get_singleton()->shell_open(p_url);
} }
void EditorAddonLibraryItemDescription::configure(const String& p_title,int p_asset_id,const String& p_category,int p_category_id,const String& p_author,int p_author_id,int p_rating,const String& p_cost,const String& p_version,const String& p_description,const String& p_download_url,const String& p_browse_url) { void EditorAddonLibraryItemDescription::_preview_click(int p_id) {
for(int i=0;i<preview_images.size();i++) {
if(preview_images[i].id==p_id) {
preview_images[i].button->set_pressed(true);
if(!preview_images[i].is_video) {
if(preview_images[i].image.is_valid()) {
preview->set_texture(preview_images[i].image);
}
} else {
_link_click(preview_images[i].video_link);
}
} else {
preview_images[i].button->set_pressed(false);
}
}
}
void EditorAddonLibraryItemDescription::configure(const String& p_title,int p_asset_id,const String& p_category,int p_category_id,const String& p_author,int p_author_id,int p_rating,const String& p_cost,int p_version,const String& p_version_string,const String& p_description,const String& p_download_url,const String& p_browse_url) {
asset_id=p_asset_id; asset_id=p_asset_id;
title=p_title; title=p_title;
download_url=p_download_url; download_url=p_download_url;
item->configure(p_title,p_asset_id,p_category,p_category_id,p_author,p_author_id,p_rating,p_cost); item->configure(p_title,p_asset_id,p_category,p_category_id,p_author,p_author_id,p_rating,p_cost);
description->clear(); description->clear();
description->add_text("Version: "+p_version+"\n"); description->add_text("Version: "+p_version_string+"\n");
description->add_text("Contents: "); description->add_text("Contents: ");
description->push_meta(p_browse_url); description->push_meta(p_browse_url);
description->add_text("View Files"); description->add_text("View Files");
description->pop(); description->pop();
description->add_text("\nDescription:\n\n"); description->add_text("\nDescription:\n\n");
description->append_bbcode(p_description); description->append_bbcode(p_description);
set_title(p_title); set_title(p_title);
} }
@ -199,13 +249,19 @@ void EditorAddonLibraryItemDescription::add_preview(int p_id, bool p_video,const
Preview preview; Preview preview;
preview.id=p_id; preview.id=p_id;
preview.video_link=p_url; preview.video_link=p_url;
preview.is_video=p_video;
preview.button = memnew( Button ); preview.button = memnew( Button );
preview.button->set_flat(true); preview.button->set_flat(true);
preview.button->set_icon(get_icon("ThumbnailWait","EditorIcons")); preview.button->set_icon(get_icon("ThumbnailWait","EditorIcons"));
preview.button->set_toggle_mode(true); preview.button->set_toggle_mode(true);
preview.button->connect("pressed", this, "_preview_click", varray(p_id));
preview_hb->add_child(preview.button); preview_hb->add_child(preview.button);
if (preview_images.size()==0) if(!p_video) {
preview.button->set_pressed(true); preview.image=get_icon("ThumbnailWait","EditorIcons");
}
if(preview_images.size()==0 && !p_video) {
_preview_click(p_id);
}
preview_images.push_back(preview); preview_images.push_back(preview);
} }
@ -257,7 +313,7 @@ EditorAddonLibraryItemDescription::EditorAddonLibraryItemDescription() {
previews->add_child(preview_hb); previews->add_child(preview_hb);
get_ok()->set_text("Install"); get_ok()->set_text("Install");
get_cancel()->set_text(TTR("Close")); get_cancel()->set_text("Close");
@ -337,14 +393,9 @@ void EditorAddonLibraryItemDownload::configure(const String& p_title,int p_asset
icon->set_texture(get_icon("GodotAssetDefault","EditorIcons")); icon->set_texture(get_icon("GodotAssetDefault","EditorIcons"));
host=p_download_url; host=p_download_url;
set_process(true);
download->set_download_file(EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("tmp_asset_"+itos(p_asset_id))+".zip");
Error err = download->request(p_download_url);
ERR_FAIL_COND(err!=OK);
asset_installer->connect("confirmed",this,"_close"); asset_installer->connect("confirmed",this,"_close");
dismiss->set_normal_texture(get_icon("Close","EditorIcons")); dismiss->set_normal_texture( get_icon("Close","EditorIcons"));
_make_request();
} }
@ -392,11 +443,23 @@ void EditorAddonLibraryItemDownload::_install() {
asset_installer->open(file,1); asset_installer->open(file,1);
} }
void EditorAddonLibraryItemDownload::_make_request() {
download->cancel_request();
download->set_download_file(EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("tmp_asset_"+itos(asset_id))+".zip");
Error err = download->request(host);
if(err!=OK) {
status->set_text("Error making request");
} else {
set_process(true);
}
}
void EditorAddonLibraryItemDownload::_bind_methods() { void EditorAddonLibraryItemDownload::_bind_methods() {
ObjectTypeDB::bind_method("_http_download_completed",&EditorAddonLibraryItemDownload::_http_download_completed); ObjectTypeDB::bind_method("_http_download_completed",&EditorAddonLibraryItemDownload::_http_download_completed);
ObjectTypeDB::bind_method("_install",&EditorAddonLibraryItemDownload::_install); ObjectTypeDB::bind_method("_install",&EditorAddonLibraryItemDownload::_install);
ObjectTypeDB::bind_method("_close",&EditorAddonLibraryItemDownload::_close); ObjectTypeDB::bind_method("_close",&EditorAddonLibraryItemDownload::_close);
ObjectTypeDB::bind_method("_make_request",&EditorAddonLibraryItemDownload::_make_request);
} }
@ -442,6 +505,11 @@ EditorAddonLibraryItemDownload::EditorAddonLibraryItemDownload() {
install->set_disabled(true); install->set_disabled(true);
install->connect("pressed",this,"_install"); install->connect("pressed",this,"_install");
retry = memnew( Button );
retry->set_text("Retry");
retry->connect("pressed",this,"_make_request");
hb2->add_child(retry);
hb2->add_child(install); hb2->add_child(install);
set_custom_minimum_size(Size2(250,0)); set_custom_minimum_size(Size2(250,0));
@ -472,7 +540,7 @@ void EditorAddonLibrary::_notification(int p_what) {
error_hb->add_child(tf); error_hb->add_child(tf);
error_label->raise(); error_label->raise();
_api_request("api/configure"); _repository_changed(0);
} }
if (p_what==NOTIFICATION_PROCESS) { if (p_what==NOTIFICATION_PROCESS) {
@ -572,7 +640,7 @@ void EditorAddonLibrary::_select_category(int p_id){
} }
void EditorAddonLibrary::_select_asset(int p_id){ void EditorAddonLibrary::_select_asset(int p_id){
_api_request("api/asset","?id="+itos(p_id)); _api_request("asset/"+itos(p_id), REQUESTING_ASSET);
/* /*
if (description) { if (description) {
@ -585,33 +653,102 @@ void EditorAddonLibrary::_select_asset(int p_id){
description->popup_centered_minsize();*/ description->popup_centered_minsize();*/
} }
void EditorAddonLibrary::_image_update(bool use_cache, bool final, const ByteArray& p_data, int p_queue_id) {
Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target);
if (obj) {
bool image_set = false;
ByteArray image_data = p_data;
if(use_cache) {
String cache_filename_base = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("assetimage_"+image_queue[p_queue_id].image_url.md5_text());
FileAccess* file = FileAccess::open(cache_filename_base+".data", FileAccess::READ);
if(file) {
ByteArray cached_data;
int len=file->get_32();
cached_data.resize(len);
ByteArray::Write w=cached_data.write();
file->get_buffer(w.ptr(), len);
image_data = cached_data;
file->close();
}
}
int len=image_data.size();
ByteArray::Read r=image_data.read();
Image image(r.ptr(),len);
if (!image.empty()) {
float max_height = 10000;
switch(image_queue[p_queue_id].image_type) {
case IMAGE_QUEUE_ICON: max_height=80; break;
case IMAGE_QUEUE_THUMBNAIL: max_height=80; break;
case IMAGE_QUEUE_SCREENSHOT: max_height=345; break;
}
float scale_ratio = max_height / image.get_height();
if(scale_ratio < 1) {
image.resize(image.get_width() * scale_ratio, image.get_height() * scale_ratio, Image::INTERPOLATE_CUBIC);
}
Ref<ImageTexture> tex;
tex.instance();
tex->create_from_image(image);
obj->call("set_image",image_queue[p_queue_id].image_type,image_queue[p_queue_id].image_index,tex);
image_set = true;
}
if(!image_set && final) {
obj->call("set_image",image_queue[p_queue_id].image_type,image_queue[p_queue_id].image_index,get_icon("ErrorSign","EditorIcons"));
}
}
}
void EditorAddonLibrary::_image_request_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data,int p_queue_id) { void EditorAddonLibrary::_image_request_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data,int p_queue_id) {
ERR_FAIL_COND( !image_queue.has(p_queue_id) ); ERR_FAIL_COND( !image_queue.has(p_queue_id) );
if (p_status==HTTPRequest::RESULT_SUCCESS) { if (p_status==HTTPRequest::RESULT_SUCCESS) {
print_line("GOT IMAGE YAY!"); print_line("GOT IMAGE YAY!");
Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target);
if(p_code != HTTPClient::RESPONSE_NOT_MODIFIED) {
if (obj) { for(int i=0;i<headers.size();i++) {
int len=p_data.size(); if (headers[i].findn("ETag:")==0) { // Save etag
ByteArray::Read r=p_data.read(); String cache_filename_base = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("assetimage_"+image_queue[p_queue_id].image_url.md5_text());
String new_etag = headers[i].substr(headers[i].find(":")+1,headers[i].length()).strip_edges();
Image image(r.ptr(),len); FileAccess* file;
if (!image.empty()) {
Ref<ImageTexture> tex; file = FileAccess::open(cache_filename_base+".etag", FileAccess::WRITE);
tex.instance(); if(file) {
tex->create_from_image(image); file->store_line(new_etag);
file->close();
obj->call("set_image",image_queue[p_queue_id].image_type,image_queue[p_queue_id].image_index,tex); }
int len=p_data.size();
ByteArray::Read r=p_data.read();
file = FileAccess::open(cache_filename_base+".data", FileAccess::WRITE);
if(file) {
file->store_32(len);
file->store_buffer(r.ptr(),len);
file->close();
}
break;
}
} }
} }
_image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id);
} else { } else {
WARN_PRINTS("Error getting PNG file for asset id "+itos(image_queue[p_queue_id].asset_id)); WARN_PRINTS("Error getting PNG file for asset id "+itos(image_queue[p_queue_id].asset_id));
Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target);
if (obj) {
obj->call("set_image",image_queue[p_queue_id].image_type,image_queue[p_queue_id].image_index,get_icon("ErrorSign","EditorIcons"));
}
} }
image_queue[p_queue_id].request->queue_delete();; image_queue[p_queue_id].request->queue_delete();;
@ -629,16 +766,20 @@ void EditorAddonLibrary::_update_image_queue() {
List<int> to_delete; List<int> to_delete;
for (Map<int,ImageQueue>::Element *E=image_queue.front();E;E=E->next()) { for (Map<int,ImageQueue>::Element *E=image_queue.front();E;E=E->next()) {
if (!E->get().active && current_images<max_images) { if (!E->get().active && current_images<max_images) {
String api; String cache_filename_base = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("assetimage_"+E->get().image_url.md5_text());
switch(E->get().image_type) { Vector<String> headers;
case IMAGE_QUEUE_ICON: api="api/icon/icon.png"; break;
case IMAGE_QUEUE_SCREENSHOT: api="api/screenshot/screenshot.png"; break; if(FileAccess::exists(cache_filename_base+".etag") && FileAccess::exists(cache_filename_base+".data")) {
case IMAGE_QUEUE_THUMBNAIL: api="api/thumbnail/thumbnail.png"; break; FileAccess* file = FileAccess::open(cache_filename_base+".etag", FileAccess::READ);
if (file) {
headers.push_back("If-None-Match: " + file->get_line());
file->close();
}
} }
print_line("REQUEST ICON FOR: "+itos(E->get().asset_id)); print_line("REQUEST ICON FOR: "+itos(E->get().asset_id));
Error err = E->get().request->request(host+"/"+api+"?asset_id="+itos(E->get().asset_id)+"&index="+itos(E->get().image_index)); Error err = E->get().request->request(E->get().image_url, headers);
if (err!=OK) { if (err!=OK) {
to_delete.push_back(E->key()); to_delete.push_back(E->key());
} else { } else {
@ -657,11 +798,11 @@ void EditorAddonLibrary::_update_image_queue() {
} }
} }
void EditorAddonLibrary::_request_image(ObjectID p_for,int p_asset_id,ImageType p_type,int p_image_index) { void EditorAddonLibrary::_request_image(ObjectID p_for,String p_image_url,ImageType p_type,int p_image_index) {
ImageQueue iq; ImageQueue iq;
iq.asset_id=p_asset_id; iq.image_url=p_image_url;
iq.image_index=p_image_index; iq.image_index=p_image_index;
iq.image_type=p_type; iq.image_type=p_type;
iq.request = memnew( HTTPRequest ); iq.request = memnew( HTTPRequest );
@ -676,11 +817,17 @@ void EditorAddonLibrary::_request_image(ObjectID p_for,int p_asset_id,ImageType
add_child(iq.request); add_child(iq.request);
_image_update(true, false, ByteArray(), iq.queue_id);
_update_image_queue(); _update_image_queue();
} }
void EditorAddonLibrary::_repository_changed(int p_repository_id) {
host=repository->get_item_metadata(p_repository_id);
print_line(".." + host);
_api_request("configure", REQUESTING_CONFIG);
}
void EditorAddonLibrary::_search(int p_page) { void EditorAddonLibrary::_search(int p_page) {
@ -692,7 +839,12 @@ void EditorAddonLibrary::_search(int p_page) {
args+="&category="+itos(categories->get_item_metadata(categories->get_selected())); args+="&category="+itos(categories->get_item_metadata(categories->get_selected()));
} }
if (reverse->is_pressed()) {
args+="&reverse=true";
}
if (filter->get_text()!=String()) { if (filter->get_text()!=String()) {
args+="&filter="+filter->get_text().http_escape(); args+="&filter="+filter->get_text().http_escape();
} }
@ -701,10 +853,10 @@ void EditorAddonLibrary::_search(int p_page) {
args+="&page="+itos(p_page); args+="&page="+itos(p_page);
} }
_api_request("api/search",args); _api_request("asset",REQUESTING_SEARCH,args);
} }
HBoxContainer* EditorAddonLibrary::_make_pages(int p_page,int p_max_page,int p_page_len,int p_total_items,int p_current_items) { HBoxContainer* EditorAddonLibrary::_make_pages(int p_page,int p_page_count,int p_page_len,int p_total_items,int p_current_items) {
HBoxContainer * hbc = memnew( HBoxContainer ); HBoxContainer * hbc = memnew( HBoxContainer );
@ -713,20 +865,22 @@ HBoxContainer* EditorAddonLibrary::_make_pages(int p_page,int p_max_page,int p_p
if (from<0) if (from<0)
from=0; from=0;
int to = from+10; int to = from+10;
if (to>p_max_page) if (to>p_page_count)
to=p_max_page; to=p_page_count;
Color gray = Color(0.65,0.65,0.65); Color gray = Color(0.65,0.65,0.65);
hbc->add_spacer(); hbc->add_spacer();
hbc->add_constant_override("separation",10); hbc->add_constant_override("separation",10);
LinkButton *first = memnew( LinkButton ); if(p_page != 0) {
first->set_text("first"); LinkButton *first = memnew( LinkButton );
first->add_color_override("font_color", gray ); first->set_text("first");
first->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); first->add_color_override("font_color", gray );
first->connect("pressed",this,"_search",varray(0)); first->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
hbc->add_child(first); first->connect("pressed",this,"_search",varray(0));
hbc->add_child(first);
}
if (p_page>0) { if (p_page>0) {
LinkButton *prev = memnew( LinkButton ); LinkButton *prev = memnew( LinkButton );
@ -737,19 +891,19 @@ HBoxContainer* EditorAddonLibrary::_make_pages(int p_page,int p_max_page,int p_p
hbc->add_child(prev); hbc->add_child(prev);
} }
for(int i=from;i<=to;i++) { for(int i=from;i<to;i++) {
if (i==p_page) { if (i==p_page) {
Label *current = memnew(Label); Label *current = memnew(Label);
current->set_text(itos(i)); current->set_text(itos(i+1));
hbc->add_child(current); hbc->add_child(current);
} else { } else {
LinkButton *current = memnew( LinkButton ); LinkButton *current = memnew( LinkButton );
current->add_color_override("font_color", gray ); current->add_color_override("font_color", gray );
current->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); current->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
current->set_text(itos(i)); current->set_text(itos(i+1));
current->connect("pressed",this,"_search",varray(i)); current->connect("pressed",this,"_search",varray(i));
hbc->add_child(current); hbc->add_child(current);
@ -757,7 +911,7 @@ HBoxContainer* EditorAddonLibrary::_make_pages(int p_page,int p_max_page,int p_p
} }
} }
if (p_page<p_max_page) { if (p_page<p_page_count-1) {
LinkButton *next = memnew( LinkButton ); LinkButton *next = memnew( LinkButton );
next->set_text("next"); next->set_text("next");
next->add_color_override("font_color", gray ); next->add_color_override("font_color", gray );
@ -766,12 +920,15 @@ HBoxContainer* EditorAddonLibrary::_make_pages(int p_page,int p_max_page,int p_p
hbc->add_child(next); hbc->add_child(next);
} }
LinkButton *last = memnew( LinkButton );
last->set_text("last"); if(p_page != p_page_count-1) {
last->add_color_override("font_color", gray ); LinkButton *last = memnew( LinkButton );
last->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); last->set_text("last");
hbc->add_child(last); last->add_color_override("font_color", gray );
last->connect("pressed",this,"_search",varray(p_max_page)); last->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
hbc->add_child(last);
last->connect("pressed",this,"_search",varray(p_page_count-1));
}
Label *totals = memnew( Label ); Label *totals = memnew( Label );
totals->set_text("( "+itos(from*p_page_len)+" - "+itos(from*p_page_len+p_current_items-1)+" / "+itos(p_total_items)+" )"); totals->set_text("( "+itos(from*p_page_len)+" - "+itos(from*p_page_len+p_current_items-1)+" / "+itos(p_total_items)+" )");
@ -783,14 +940,14 @@ HBoxContainer* EditorAddonLibrary::_make_pages(int p_page,int p_max_page,int p_p
} }
void EditorAddonLibrary::_api_request(const String& p_request,const String& p_arguments) { void EditorAddonLibrary::_api_request(const String& p_request, RequestType p_request_type, const String& p_arguments) {
if (requesting!=REQUESTING_NONE) { if (requesting!=REQUESTING_NONE) {
request->cancel_request(); request->cancel_request();
} }
requesting=p_request_type;
error_hb->hide(); error_hb->hide();
current_request=p_request;
request->request(host+"/"+p_request+p_arguments); request->request(host+"/"+p_request+p_arguments);
} }
@ -856,165 +1013,173 @@ void EditorAddonLibrary::_http_request_completed(int p_status, int p_code, const
print_line(Variant(d).get_construct_string()); print_line(Variant(d).get_construct_string());
if (current_request=="api/configure") { RequestType requested = requesting;
requesting=REQUESTING_NONE;
categories->clear(); switch(requested) {
categories->add_item("All"); case REQUESTING_CONFIG: {
categories->set_item_metadata(0,0);
if (d.has("categories")) { categories->clear();
Array clist = d["categories"]; categories->add_item("All");
for(int i=0;i<clist.size();i++) { categories->set_item_metadata(0,0);
Dictionary cat = clist[i]; if (d.has("categories")) {
if (!cat.has("name") || !cat.has("id")) Array clist = d["categories"];
continue; for(int i=0;i<clist.size();i++) {
String name=cat["name"]; Dictionary cat = clist[i];
int id=cat["id"]; if (!cat.has("name") || !cat.has("id"))
categories->add_item(name); continue;
categories->set_item_metadata( categories->get_item_count() -1, id); String name=cat["name"];
int id=cat["id"];
categories->add_item(name);
categories->set_item_metadata( categories->get_item_count() -1, id);
category_map[cat["id"]] = name;
}
} }
}
_search(); _search();
} else if (current_request=="api/search") { } break;
case REQUESTING_SEARCH: {
if (asset_items) {
memdelete(asset_items);
}
if (asset_items) { if (asset_top_page) {
memdelete(asset_items); memdelete(asset_top_page);
} }
if (asset_top_page) { if (asset_bottom_page) {
memdelete(asset_top_page); memdelete(asset_bottom_page);
} }
if (asset_bottom_page) { int page=0;
memdelete(asset_bottom_page); int pages=1;
} int page_len=10;
int total_items=1;
int page=0; Array result;
int pages=1;
int page_len=10;
int total_items=1;
Array result;
if (d.has("page")) { if (d.has("page")) {
page=d["page"]; page=d["page"];
} }
if (d.has("pages")) { if (d.has("pages")) {
pages=d["pages"]; pages=d["pages"];
} }
if (d.has("page_length")) { if (d.has("page_length")) {
page_len=d["page_length"]; page_len=d["page_length"];
} }
if (d.has("total")) { if (d.has("total")) {
total_items=d["total"]; total_items=d["total"];
} }
if (d.has("result")) { if (d.has("result")) {
result=d["result"]; result=d["result"];
} }
asset_top_page = _make_pages(page,pages,page_len,total_items,result.size()); asset_top_page = _make_pages(page,pages,page_len,total_items,result.size());
library_vb->add_child(asset_top_page); library_vb->add_child(asset_top_page);
asset_items = memnew( GridContainer ); asset_items = memnew( GridContainer );
asset_items->set_columns(2); asset_items->set_columns(2);
asset_items->add_constant_override("hseparation",10); asset_items->add_constant_override("hseparation",10);
asset_items->add_constant_override("vseparation",10); asset_items->add_constant_override("vseparation",10);
library_vb->add_child(asset_items); library_vb->add_child(asset_items);
asset_bottom_page = _make_pages(page,pages,page_len,total_items,result.size()); asset_bottom_page = _make_pages(page,pages,page_len,total_items,result.size());
library_vb->add_child(asset_bottom_page); library_vb->add_child(asset_bottom_page);
for(int i=0;i<result.size();i++) { for(int i=0;i<result.size();i++) {
Dictionary r = result[i]; Dictionary r = result[i];
ERR_CONTINUE(!r.has("title")); ERR_CONTINUE(!r.has("title"));
ERR_CONTINUE(!r.has("asset_id")); ERR_CONTINUE(!r.has("asset_id"));
ERR_CONTINUE(!r.has("author")); ERR_CONTINUE(!r.has("author"));
ERR_CONTINUE(!r.has("author_id")); ERR_CONTINUE(!r.has("author_id"));
ERR_CONTINUE(!r.has("category")); ERR_CONTINUE(!r.has("category_id"));
ERR_CONTINUE(!r.has("category_id")); ERR_FAIL_COND(!category_map.has(r["category_id"]));
ERR_CONTINUE(!r.has("rating")); ERR_CONTINUE(!r.has("rating"));
ERR_CONTINUE(!r.has("cost")); ERR_CONTINUE(!r.has("cost"));
EditorAssetLibraryItem *item = memnew( EditorAssetLibraryItem ); EditorAssetLibraryItem *item = memnew( EditorAssetLibraryItem );
asset_items->add_child(item); asset_items->add_child(item);
item->configure(r["title"],r["asset_id"],r["category"],r["category_id"],r["author"],r["author_id"],r["rating"],r["cost"]); item->configure(r["title"],r["asset_id"],category_map[r["category_id"]],r["category_id"],r["author"],r["author_id"],r["rating"],r["cost"]);
item->connect("asset_selected",this,"_select_asset"); item->connect("asset_selected",this,"_select_asset");
item->connect("author_selected",this,"_select_author");
item->connect("category_selected",this,"_select_category");
if(r.has("icon_url") && r["icon_url"] != "") {
_request_image(item->get_instance_ID(),r["icon_url"],IMAGE_QUEUE_ICON,0);
}
}
} break;
case REQUESTING_ASSET: {
Dictionary r = d;
ERR_FAIL_COND(!r.has("title"));
ERR_FAIL_COND(!r.has("asset_id"));
ERR_FAIL_COND(!r.has("author"));
ERR_FAIL_COND(!r.has("author_id"));
ERR_FAIL_COND(!r.has("version"));
ERR_FAIL_COND(!r.has("version_string"));
ERR_FAIL_COND(!r.has("category_id"));
ERR_FAIL_COND(!category_map.has(r["category_id"]));
ERR_FAIL_COND(!r.has("rating"));
ERR_FAIL_COND(!r.has("cost"));
ERR_FAIL_COND(!r.has("description"));
ERR_FAIL_COND(!r.has("download_url"));
ERR_FAIL_COND(!r.has("browse_url"));
if (description) {
memdelete(description);
}
description = memnew( EditorAddonLibraryItemDescription );
add_child(description);
description->popup_centered_minsize();
description->connect("confirmed",this,"_install_asset");
description->configure(r["title"],r["asset_id"],category_map[r["category_id"]],r["category_id"],r["author"],r["author_id"],r["rating"],r["cost"],r["version"],r["version_string"],r["description"],r["download_url"],r["browse_url"]);
/*item->connect("asset_selected",this,"_select_asset");
item->connect("author_selected",this,"_select_author"); item->connect("author_selected",this,"_select_author");
item->connect("category_selected",this,"_category_selected"); item->connect("category_selected",this,"_category_selected");*/
_request_image(item->get_instance_ID(),r["asset_id"],IMAGE_QUEUE_ICON,0);
}
} else if (current_request=="api/asset") {
ERR_FAIL_COND(!d.has("info"));
Dictionary r = d["info"];
r["download_url"]="https://github.com/reduz/godot-test-addon/archive/master.zip";
r["browse_url"]="https://github.com/reduz/godot-test-addon";
r["version"]="1.1";
ERR_FAIL_COND(!r.has("title"));
ERR_FAIL_COND(!r.has("asset_id"));
ERR_FAIL_COND(!r.has("author"));
ERR_FAIL_COND(!r.has("author_id"));
ERR_FAIL_COND(!r.has("version"));
ERR_FAIL_COND(!r.has("category"));
ERR_FAIL_COND(!r.has("category_id"));
ERR_FAIL_COND(!r.has("rating"));
ERR_FAIL_COND(!r.has("cost"));
ERR_FAIL_COND(!r.has("description"));
ERR_FAIL_COND(!r.has("download_url"));
ERR_FAIL_COND(!r.has("browse_url"));
if (description) {
memdelete(description);
}
description = memnew( EditorAddonLibraryItemDescription );
add_child(description);
description->popup_centered_minsize();
description->connect("confirmed",this,"_install_asset");
description->configure(r["title"],r["asset_id"],r["category"],r["category_id"],r["author"],r["author_id"],r["rating"],r["cost"],r["version"],r["description"],r["download_url"],r["browse_url"]);
/*item->connect("asset_selected",this,"_select_asset");
item->connect("author_selected",this,"_select_author");
item->connect("category_selected",this,"_category_selected");*/
_request_image(description->get_instance_ID(),r["asset_id"],IMAGE_QUEUE_ICON,0);
if (d.has("previews")) {
Array previews = d["previews"];
for(int i=0;i<previews.size();i++) {
Dictionary p=previews[i];
ERR_CONTINUE(!p.has("id"));
bool is_video=p.has("type") && String(p["type"])=="video";
String video_url;
if (is_video && p.has("link")) {
video_url="link";
}
int id=p["id"];
description->add_preview(id,is_video,video_url);
_request_image(description->get_instance_ID(),r["asset_id"],IMAGE_QUEUE_THUMBNAIL,id);
if (i==0) {
_request_image(description->get_instance_ID(),r["asset_id"],IMAGE_QUEUE_SCREENSHOT,id);
}
if(r.has("icon_url") && r["icon_url"] != "") {
_request_image(description->get_instance_ID(),r["icon_url"],IMAGE_QUEUE_ICON,0);
} }
}
if (d.has("previews")) {
Array previews = d["previews"];
for(int i=0;i<previews.size();i++) {
Dictionary p=previews[i];
ERR_CONTINUE(!p.has("type"));
ERR_CONTINUE(!p.has("link"));
bool is_video=p.has("type") && String(p["type"])=="video";
String video_url;
if (is_video && p.has("link")) {
video_url=p["link"];
}
description->add_preview(i,is_video,video_url);
if(p.has("thumbnail")) {
_request_image(description->get_instance_ID(),p["thumbnail"],IMAGE_QUEUE_THUMBNAIL,i);
}
if(is_video) {
//_request_image(description->get_instance_ID(),p["link"],IMAGE_QUEUE_SCREENSHOT,i);
} else {
_request_image(description->get_instance_ID(),p["link"],IMAGE_QUEUE_SCREENSHOT,i);
}
}
}
} break;
default: break;
} }
} }
@ -1059,6 +1224,7 @@ void EditorAddonLibrary::_bind_methods() {
ObjectTypeDB::bind_method("_manage_plugins",&EditorAddonLibrary::_manage_plugins); ObjectTypeDB::bind_method("_manage_plugins",&EditorAddonLibrary::_manage_plugins);
ObjectTypeDB::bind_method("_asset_open",&EditorAddonLibrary::_asset_open); ObjectTypeDB::bind_method("_asset_open",&EditorAddonLibrary::_asset_open);
ObjectTypeDB::bind_method("_asset_file_selected",&EditorAddonLibrary::_asset_file_selected); ObjectTypeDB::bind_method("_asset_file_selected",&EditorAddonLibrary::_asset_file_selected);
ObjectTypeDB::bind_method("_repository_changed",&EditorAddonLibrary::_repository_changed);
} }
@ -1090,19 +1256,19 @@ EditorAddonLibrary::EditorAddonLibrary() {
search_hb->add_child(filter); search_hb->add_child(filter);
filter->set_h_size_flags(SIZE_EXPAND_FILL); filter->set_h_size_flags(SIZE_EXPAND_FILL);
filter->connect("text_entered",this,"_search"); filter->connect("text_entered",this,"_search");
search = memnew( Button(TTR("Search"))); search = memnew( Button("Search"));
search->connect("pressed",this,"_search"); search->connect("pressed",this,"_search");
search_hb->add_child(search); search_hb->add_child(search);
search_hb->add_child(memnew( VSeparator )); search_hb->add_child(memnew( VSeparator ));
Button * open_asset = memnew( Button ); Button * open_asset = memnew( Button );
open_asset->set_text(TTR("Import")); open_asset->set_text("Import");
search_hb->add_child(open_asset); search_hb->add_child(open_asset);
open_asset->connect("pressed",this,"_asset_open"); open_asset->connect("pressed",this,"_asset_open");
Button * plugins = memnew( Button ); Button * plugins = memnew( Button );
plugins->set_text(TTR("Plugins")); plugins->set_text("Plugins");
search_hb->add_child(plugins); search_hb->add_child(plugins);
plugins->connect("pressed",this,"_manage_plugins"); plugins->connect("pressed",this,"_manage_plugins");
@ -1142,7 +1308,12 @@ EditorAddonLibrary::EditorAddonLibrary() {
search_hb2->add_child( memnew( Label(TTR("Site:")+" "))); search_hb2->add_child( memnew( Label(TTR("Site:")+" ")));
repository = memnew( OptionButton ); repository = memnew( OptionButton );
repository->add_item("Localhost"); // TODO: Maybe remove?
repository->set_item_metadata(0, "http://127.0.0.1/addonlib/api");
repository->add_item("Godot"); repository->add_item("Godot");
repository->set_item_metadata(1, "http://godotengine.org/addonlib/api");
repository->connect("item_selected",this,"_repository_changed");
search_hb2->add_child(repository); search_hb2->add_child(repository);
repository->set_h_size_flags(SIZE_EXPAND_FILL); repository->set_h_size_flags(SIZE_EXPAND_FILL);
@ -1199,7 +1370,6 @@ EditorAddonLibrary::EditorAddonLibrary() {
add_child(request); add_child(request);
request->connect("request_completed",this,"_http_request_completed"); request->connect("request_completed",this,"_http_request_completed");
last_queue_id=0; last_queue_id=0;
library_vb->add_constant_override("separation",20); library_vb->add_constant_override("separation",20);
@ -1218,8 +1388,6 @@ EditorAddonLibrary::EditorAddonLibrary() {
description = NULL; description = NULL;
//host="http://localhost:8000";
host="http://godotengine.org/addonlib";
set_process(true); set_process(true);
downloads_scroll = memnew( ScrollContainer ); downloads_scroll = memnew( ScrollContainer );
@ -1245,7 +1413,7 @@ EditorAddonLibrary::EditorAddonLibrary() {
/////// ///////
void AddonEditorPlugin::make_visible(bool p_visible) { void AssetLibraryEditorPlugin::make_visible(bool p_visible) {
if (p_visible) { if (p_visible) {
@ -1257,7 +1425,7 @@ void AddonEditorPlugin::make_visible(bool p_visible) {
} }
AddonEditorPlugin::AddonEditorPlugin(EditorNode *p_node) { AssetLibraryEditorPlugin::AssetLibraryEditorPlugin(EditorNode *p_node) {
editor=p_node; editor=p_node;
addon_library = memnew( EditorAddonLibrary ); addon_library = memnew( EditorAddonLibrary );
@ -1268,6 +1436,6 @@ AddonEditorPlugin::AddonEditorPlugin(EditorNode *p_node) {
} }
AddonEditorPlugin::~AddonEditorPlugin() { AssetLibraryEditorPlugin::~AssetLibraryEditorPlugin() {
} }

View File

@ -1,5 +1,33 @@
#ifndef ADDON_EDITOR_PLUGIN_H /*************************************************************************/
#define ADDON_EDITOR_PLUGIN_H /* asset_library_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef ASSET_LIBRARY_EDITOR_PLUGIN_H
#define ASSET_LIBRARY_EDITOR_PLUGIN_H
#include "editor_plugin.h" #include "editor_plugin.h"
@ -69,8 +97,10 @@ class EditorAddonLibraryItemDescription : public ConfirmationDialog {
struct Preview { struct Preview {
int id; int id;
bool is_video;
String video_link; String video_link;
Button *button; Button *button;
Ref<Texture> image;
}; };
Vector<Preview> preview_images; Vector<Preview> preview_images;
@ -84,12 +114,13 @@ class EditorAddonLibraryItemDescription : public ConfirmationDialog {
Ref<Texture> icon; Ref<Texture> icon;
void _link_click(const String& p_url); void _link_click(const String& p_url);
void _preview_click(int p_index);
protected: protected:
static void _bind_methods(); static void _bind_methods();
public: public:
void configure(const String& p_title,int p_asset_id,const String& p_category,int p_category_id,const String& p_author,int p_author_id,int p_rating,const String& p_cost,const String& p_version,const String& p_description,const String& p_download_url,const String& p_browse_url); void configure(const String& p_title,int p_asset_id,const String& p_category,int p_category_id,const String& p_author,int p_author_id,int p_rating,const String& p_cost,int p_version,const String& p_version_string,const String& p_description,const String& p_download_url,const String& p_browse_url);
void add_preview(int p_id, bool p_video,const String& p_url); void add_preview(int p_id, bool p_video,const String& p_url);
String get_title() { return title; } String get_title() { return title; }
@ -109,6 +140,7 @@ class EditorAddonLibraryItemDownload : public PanelContainer {
Label* title; Label* title;
ProgressBar *progress; ProgressBar *progress;
Button *install; Button *install;
Button *retry;
TextureButton *dismiss; TextureButton *dismiss;
AcceptDialog *download_error; AcceptDialog *download_error;
@ -124,6 +156,7 @@ class EditorAddonLibraryItemDownload : public PanelContainer {
void _close(); void _close();
void _install(); void _install();
void _make_request();
void _http_download_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data); void _http_download_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data);
protected: protected:
@ -202,6 +235,7 @@ class EditorAddonLibrary : public PanelContainer {
int asset_id; int asset_id;
ImageType image_type; ImageType image_type;
int image_index; int image_index;
String image_url;
HTTPRequest *request; HTTPRequest *request;
ObjectID target; ObjectID target;
}; };
@ -209,27 +243,27 @@ class EditorAddonLibrary : public PanelContainer {
int last_queue_id; int last_queue_id;
Map<int,ImageQueue> image_queue; Map<int,ImageQueue> image_queue;
void _image_update(bool use_cache, bool final, const ByteArray& p_data, int p_queue_id);
void _image_request_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data, int p_queue_id); void _image_request_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data, int p_queue_id);
void _request_image(ObjectID p_for,String p_image_url,ImageType p_type,int p_image_index);
void _request_image(ObjectID p_for,int p_asset_id,ImageType p_type,int p_image_index);
void _update_image_queue(); void _update_image_queue();
HBoxContainer* _make_pages(int p_page, int p_max_page, int p_page_len, int p_total_items, int p_current_items); HBoxContainer* _make_pages(int p_page, int p_page_count, int p_page_len, int p_total_items, int p_current_items);
// //
EditorAddonLibraryItemDescription *description; EditorAddonLibraryItemDescription *description;
String current_request;
// //
enum RequestType { enum RequestType {
REQUESTING_NONE, REQUESTING_NONE,
REQUESTING_CONFIG, REQUESTING_CONFIG,
REQUESTING_SEARCH,
REQUESTING_ASSET,
}; };
RequestType requesting; RequestType requesting;
Dictionary category_map;
ScrollContainer *downloads_scroll; ScrollContainer *downloads_scroll;
@ -246,10 +280,12 @@ class EditorAddonLibrary : public PanelContainer {
void _manage_plugins(); void _manage_plugins();
void _search(int p_page=0); void _search(int p_page=0);
void _api_request(const String& p_request, const String &p_arguments=""); void _api_request(const String& p_request, RequestType p_request_type, const String &p_arguments="");
void _http_request_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data); void _http_request_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data);
void _http_download_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data); void _http_download_completed(int p_status, int p_code, const StringArray& headers, const ByteArray& p_data);
void _repository_changed(int p_repository_id);
friend class EditorAddonLibraryItemDescription; friend class EditorAddonLibraryItemDescription;
friend class EditorAssetLibraryItem; friend class EditorAssetLibraryItem;
protected: protected:
@ -260,9 +296,9 @@ public:
EditorAddonLibrary(); EditorAddonLibrary();
}; };
class AddonEditorPlugin : public EditorPlugin { class AssetLibraryEditorPlugin : public EditorPlugin {
OBJ_TYPE( AddonEditorPlugin, EditorPlugin ); OBJ_TYPE( AssetLibraryEditorPlugin, EditorPlugin );
EditorAddonLibrary *addon_library; EditorAddonLibrary *addon_library;
EditorNode *editor; EditorNode *editor;
@ -278,8 +314,8 @@ public:
//virtual Dictionary get_state() const; //virtual Dictionary get_state() const;
//virtual void set_state(const Dictionary& p_state); //virtual void set_state(const Dictionary& p_state);
AddonEditorPlugin(EditorNode *p_node); AssetLibraryEditorPlugin(EditorNode *p_node);
~AddonEditorPlugin(); ~AssetLibraryEditorPlugin();
}; };

View File

@ -61,7 +61,7 @@
#include "plugins/sprite_frames_editor_plugin.h" #include "plugins/sprite_frames_editor_plugin.h"
#include "plugins/texture_region_editor_plugin.h" #include "plugins/texture_region_editor_plugin.h"
#include "plugins/canvas_item_editor_plugin.h" #include "plugins/canvas_item_editor_plugin.h"
#include "addon_editor_plugin.h" #include "asset_library_editor_plugin.h"
#include "plugins/spatial_editor_plugin.h" #include "plugins/spatial_editor_plugin.h"
#include "plugins/sample_editor_plugin.h" #include "plugins/sample_editor_plugin.h"
#include "plugins/texture_editor_plugin.h" #include "plugins/texture_editor_plugin.h"
@ -6378,7 +6378,7 @@ EditorNode::EditorNode() {
add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) ); add_editor_plugin( memnew( CanvasItemEditorPlugin(this) ) );
add_editor_plugin( memnew( SpatialEditorPlugin(this) ) ); add_editor_plugin( memnew( SpatialEditorPlugin(this) ) );
add_editor_plugin( memnew( ScriptEditorPlugin(this) ) ); add_editor_plugin( memnew( ScriptEditorPlugin(this) ) );
//add_editor_plugin( memnew( AddonEditorPlugin(this) ) ); add_editor_plugin( memnew( AssetLibraryEditorPlugin(this) ) );
//more visually meaningful to have this later //more visually meaningful to have this later
raise_bottom_panel_item(AnimationPlayerEditor::singleton); raise_bottom_panel_item(AnimationPlayerEditor::singleton);