Compare commits
28 commits
c5c8b0f56d
...
6b255ceb59
Author | SHA1 | Date | |
---|---|---|---|
6b255ceb59 | |||
8f68ae45ce | |||
757e1029b4 | |||
abe3abaf70 | |||
56de603bc7 | |||
d3c1469dd8 | |||
ba97c7c574 | |||
665f8f6151 | |||
58cd6528a3 | |||
b1670f31e8 | |||
c4c767fda9 | |||
c58c262ccf | |||
e00aa46911 | |||
cc1da0804e | |||
ebb1b4d3a5 | |||
5014c82541 | |||
d258e2b818 | |||
2135fa957c | |||
17cf627cb9 | |||
14125d755a | |||
1e8111c2dd | |||
f28f9f36a2 | |||
2b43098187 | |||
763bb230a7 | |||
1b3b88223d | |||
e51d00708b | |||
9c4f2c7495 | |||
092c3c748d |
31 changed files with 527 additions and 196 deletions
BIN
assets/font/Arial.TTF
Normal file
BIN
assets/font/Arial.TTF
Normal file
Binary file not shown.
BIN
assets/icons/message.png
Normal file
BIN
assets/icons/message.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 413 B |
|
@ -3,10 +3,10 @@
|
||||||
// I/O
|
// I/O
|
||||||
|
|
||||||
out vec4 fColor;
|
out vec4 fColor;
|
||||||
uniform vec3 aColor;
|
uniform vec4 aColor;
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
fColor = vec4(aColor, 1.0);
|
fColor = aColor;
|
||||||
}
|
}
|
|
@ -77,7 +77,7 @@ int main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void errorCatcher(int id, const char* str) {
|
void errorCatcher(int id, const char* str) {
|
||||||
Logger::fatalError(std::format("GLFW Error: [{}] {}", id, str));
|
Logger::fatalErrorf("GLFW Error: [{}] {}", id, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
float lastTime;
|
float lastTime;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
extern const char* WRAPPER_SRC; // TODO: Move this to a shared header
|
|
||||||
int script_errhandler(lua_State*); // extern
|
int script_errhandler(lua_State*); // extern
|
||||||
|
|
||||||
SignalSource::SignalSource() : std::shared_ptr<Signal>(std::make_shared<Signal>()) {}
|
SignalSource::SignalSource() : std::shared_ptr<Signal>(std::make_shared<Signal>()) {}
|
||||||
|
@ -39,30 +38,11 @@ LuaSignalConnection::~LuaSignalConnection() {
|
||||||
luaL_unref(state, LUA_REGISTRYINDEX, thread);
|
luaL_unref(state, LUA_REGISTRYINDEX, thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void stackdump(lua_State* L) {
|
|
||||||
printf("%d\n", lua_gettop(L));
|
|
||||||
fflush(stdout);
|
|
||||||
lua_getfield(L, LUA_GLOBALSINDEX, "tostring");
|
|
||||||
for (int i = lua_gettop(L)-1; i >= 1; i--) {
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_pushvalue(L, i);
|
|
||||||
lua_call(L, 1, 1);
|
|
||||||
const char* str = lua_tostring(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
printf("%s: %s\n", lua_typename(L, lua_type(L, i)), str);
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
printf("\n\n");
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void LuaSignalConnection::Call(std::vector<Variant> args) {
|
void LuaSignalConnection::Call(std::vector<Variant> args) {
|
||||||
lua_State* thread = lua_newthread(state);
|
lua_State* thread = lua_newthread(state);
|
||||||
|
|
||||||
// Push wrapepr as thread function
|
// Push wrapepr as thread function
|
||||||
luaL_loadbuffer(thread, WRAPPER_SRC, strlen(WRAPPER_SRC), "=PCALL_WRAPPER");
|
lua_getfield(thread, LUA_REGISTRYINDEX, "LuaPCallWrapper");
|
||||||
|
|
||||||
// Push function as upvalue for wrapper
|
// Push function as upvalue for wrapper
|
||||||
lua_rawgeti(thread, LUA_REGISTRYINDEX, function);
|
lua_rawgeti(thread, LUA_REGISTRYINDEX, function);
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
static std::ofstream logStream;
|
static std::ofstream logStream;
|
||||||
static std::vector<Logger::LogListener> logListeners;
|
static std::vector<Logger::LogListener> logListeners;
|
||||||
static std::vector<Logger::TraceLogListener> traceLogListeners;
|
|
||||||
std::string Logger::currentLogDir = "NULL";
|
std::string Logger::currentLogDir = "NULL";
|
||||||
|
|
||||||
void Logger::init() {
|
void Logger::init() {
|
||||||
|
@ -28,7 +27,7 @@ void Logger::finish() {
|
||||||
logStream.close();
|
logStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::log(std::string message, Logger::LogLevel logLevel) {
|
void Logger::log(std::string message, Logger::LogLevel logLevel, ScriptSource source) {
|
||||||
std::string logLevelStr = logLevel == Logger::LogLevel::INFO ? "INFO" :
|
std::string logLevelStr = logLevel == Logger::LogLevel::INFO ? "INFO" :
|
||||||
logLevel == Logger::LogLevel::DEBUG ? "DEBUG" :
|
logLevel == Logger::LogLevel::DEBUG ? "DEBUG" :
|
||||||
logLevel == Logger::LogLevel::TRACE ? "TRACE" :
|
logLevel == Logger::LogLevel::TRACE ? "TRACE" :
|
||||||
|
@ -44,7 +43,7 @@ void Logger::log(std::string message, Logger::LogLevel logLevel) {
|
||||||
printf("%s\n", formattedLogLine.c_str());
|
printf("%s\n", formattedLogLine.c_str());
|
||||||
|
|
||||||
for (Logger::LogListener listener : logListeners) {
|
for (Logger::LogListener listener : logListeners) {
|
||||||
listener(logLevel, message);
|
listener(logLevel, message, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logLevel == Logger::LogLevel::FATAL_ERROR) {
|
if (logLevel == Logger::LogLevel::FATAL_ERROR) {
|
||||||
|
@ -52,20 +51,6 @@ void Logger::log(std::string message, Logger::LogLevel logLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::trace(std::string source, int line, void* userData) {
|
|
||||||
std::string message = "'" + source + "' Line " + std::to_string(line);
|
|
||||||
|
|
||||||
log(message, Logger::LogLevel::TRACE);
|
|
||||||
|
|
||||||
for (Logger::TraceLogListener listener : traceLogListeners) {
|
|
||||||
listener(message, source, line, userData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::addLogListener(Logger::LogListener listener) {
|
void Logger::addLogListener(Logger::LogListener listener) {
|
||||||
logListeners.push_back(listener);
|
logListeners.push_back(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::addLogListener(Logger::TraceLogListener listener) {
|
|
||||||
traceLogListeners.push_back(listener);
|
|
||||||
}
|
|
|
@ -1,9 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <format>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
class Script;
|
||||||
|
|
||||||
namespace Logger {
|
namespace Logger {
|
||||||
enum class LogLevel {
|
enum class LogLevel {
|
||||||
INFO,
|
INFO,
|
||||||
|
@ -14,32 +16,37 @@ namespace Logger {
|
||||||
FATAL_ERROR,
|
FATAL_ERROR,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(LogLevel logLevel, std::string message)> LogListener;
|
struct ScriptSource {
|
||||||
typedef std::function<void(std::string message, std::string source, int line, void* userData)> TraceLogListener;
|
std::shared_ptr<Script> script;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void(LogLevel logLevel, std::string message, ScriptSource source)> LogListener;
|
||||||
|
|
||||||
extern std::string currentLogDir;
|
extern std::string currentLogDir;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void finish();
|
void finish();
|
||||||
void addLogListener(LogListener);
|
void addLogListener(LogListener);
|
||||||
void addLogListener(TraceLogListener);
|
|
||||||
|
|
||||||
void log(std::string message, LogLevel logLevel);
|
void log(std::string message, LogLevel logLevel, ScriptSource source = {});
|
||||||
inline void info(std::string message) { log(message, LogLevel::INFO); }
|
inline void info(std::string message) { log(message, LogLevel::INFO); }
|
||||||
inline void debug(std::string message) { log(message, LogLevel::DEBUG); }
|
inline void debug(std::string message) { log(message, LogLevel::DEBUG); }
|
||||||
inline void warning(std::string message) { log(message, LogLevel::WARNING); }
|
inline void warning(std::string message) { log(message, LogLevel::WARNING); }
|
||||||
inline void error(std::string message) { log(message, LogLevel::ERROR); }
|
inline void error(std::string message) { log(message, LogLevel::ERROR); }
|
||||||
inline void fatalError(std::string message) { log(message, LogLevel::FATAL_ERROR); }
|
inline void fatalError(std::string message) { log(message, LogLevel::FATAL_ERROR); }
|
||||||
|
inline void trace(std::string message) { log(message, LogLevel::TRACE); };
|
||||||
|
|
||||||
inline void traceStart() { log("Stack start", LogLevel::TRACE); }
|
template <typename ...Args>
|
||||||
inline void traceEnd() { log("Stack end", LogLevel::TRACE); }
|
void scriptLogf(std::string format, LogLevel logLevel, ScriptSource source, Args&&... args) {
|
||||||
void trace(std::string source, int line, void* userData = nullptr);
|
char message[200];
|
||||||
|
sprintf(message, format.c_str(), args...);
|
||||||
|
log(message, logLevel, source);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ...Args>
|
template <typename ...Args>
|
||||||
void logf(std::string format, LogLevel logLevel, Args&&... args) {
|
void logf(std::string format, LogLevel logLevel, Args&&... args) {
|
||||||
char message[200];
|
scriptLogf(format, logLevel, {}, args...);
|
||||||
sprintf(message, format.c_str(), args...);
|
|
||||||
log(message, logLevel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ...Args> inline void infof(std::string format, Args&&... args) { logf(format, LogLevel::INFO, args...); }
|
template <typename ...Args> inline void infof(std::string format, Args&&... args) { logf(format, LogLevel::INFO, args...); }
|
||||||
|
|
4
core/src/objects/hint.cpp
Normal file
4
core/src/objects/hint.cpp
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#include "hint.h"
|
||||||
|
|
||||||
|
Hint::Hint(): Message(&TYPE) {}
|
||||||
|
Hint::~Hint() = default;
|
18
core/src/objects/hint.h
Normal file
18
core/src/objects/hint.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "objects/annotation.h"
|
||||||
|
#include "objects/base/instance.h"
|
||||||
|
#include "objects/message.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Dims the player's screen and displays some centered text
|
||||||
|
class DEF_INST_(explorer_icon="message") Hint : public Message {
|
||||||
|
AUTOGEN_PREAMBLE
|
||||||
|
|
||||||
|
public:
|
||||||
|
Hint();
|
||||||
|
~Hint();
|
||||||
|
|
||||||
|
static inline std::shared_ptr<Hint> New() { return std::make_shared<Hint>(); };
|
||||||
|
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Hint>(); };
|
||||||
|
};
|
5
core/src/objects/message.cpp
Normal file
5
core/src/objects/message.cpp
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#include "message.h"
|
||||||
|
|
||||||
|
Message::Message(const InstanceType* type) : Instance(type) {}
|
||||||
|
Message::Message(): Instance(&TYPE) {}
|
||||||
|
Message::~Message() = default;
|
21
core/src/objects/message.h
Normal file
21
core/src/objects/message.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "objects/annotation.h"
|
||||||
|
#include "objects/base/instance.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Dims the player's screen and displays some centered text
|
||||||
|
class DEF_INST_(explorer_icon="message") Message : public Instance {
|
||||||
|
AUTOGEN_PREAMBLE
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Message(const InstanceType* type);
|
||||||
|
public:
|
||||||
|
Message();
|
||||||
|
~Message();
|
||||||
|
|
||||||
|
DEF_PROP std::string text;
|
||||||
|
|
||||||
|
static inline std::shared_ptr<Message> New() { return std::make_shared<Message>(); };
|
||||||
|
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Message>(); };
|
||||||
|
};
|
|
@ -1,9 +1,11 @@
|
||||||
#include "meta.h"
|
#include "meta.h"
|
||||||
#include "objects/folder.h"
|
#include "objects/folder.h"
|
||||||
|
#include "objects/hint.h"
|
||||||
#include "objects/joint/jointinstance.h"
|
#include "objects/joint/jointinstance.h"
|
||||||
#include "objects/joint/rotate.h"
|
#include "objects/joint/rotate.h"
|
||||||
#include "objects/joint/rotatev.h"
|
#include "objects/joint/rotatev.h"
|
||||||
#include "objects/joint/weld.h"
|
#include "objects/joint/weld.h"
|
||||||
|
#include "objects/message.h"
|
||||||
#include "objects/service/jointsservice.h"
|
#include "objects/service/jointsservice.h"
|
||||||
#include "objects/model.h"
|
#include "objects/model.h"
|
||||||
#include "objects/part.h"
|
#include "objects/part.h"
|
||||||
|
@ -27,6 +29,8 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||||
{ "JointInstance", &JointInstance::TYPE },
|
{ "JointInstance", &JointInstance::TYPE },
|
||||||
{ "Script", &Script::TYPE },
|
{ "Script", &Script::TYPE },
|
||||||
{ "Model", &Model::TYPE },
|
{ "Model", &Model::TYPE },
|
||||||
|
{ "Message", &Message::TYPE },
|
||||||
|
{ "Hint", &Hint::TYPE },
|
||||||
// { "Folder", &Folder::TYPE },
|
// { "Folder", &Folder::TYPE },
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "script.h"
|
#include "script.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "datatypes/variant.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
|
@ -12,13 +13,8 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
int script_wait(lua_State*);
|
|
||||||
int script_delay(lua_State*);
|
|
||||||
int script_errhandler(lua_State*);
|
int script_errhandler(lua_State*);
|
||||||
|
|
||||||
// TODO: Move this to a shared header
|
|
||||||
const char* WRAPPER_SRC = "local func, errhandler = ... return function(...) local args = {...} xpcall(function() func(unpack(args)) end, errhandler) end";
|
|
||||||
|
|
||||||
Script::Script(): Instance(&TYPE) {
|
Script::Script(): Instance(&TYPE) {
|
||||||
source = "print(\"Hello, world!\")";
|
source = "print(\"Hello, world!\")";
|
||||||
}
|
}
|
||||||
|
@ -36,30 +32,8 @@ void Script::Run() {
|
||||||
this->thread = lua_newthread(L);
|
this->thread = lua_newthread(L);
|
||||||
lua_State* Lt = thread;
|
lua_State* Lt = thread;
|
||||||
|
|
||||||
// Initialize script globals
|
|
||||||
lua_getglobal(Lt, "_G");
|
|
||||||
|
|
||||||
InstanceRef(shared_from_this()).PushLuaValue(Lt);
|
|
||||||
lua_setfield(Lt, -2, "script");
|
|
||||||
|
|
||||||
InstanceRef(dataModel().value()).PushLuaValue(Lt);
|
|
||||||
lua_setfield(Lt, -2, "game");
|
|
||||||
|
|
||||||
InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(Lt);
|
|
||||||
lua_setfield(Lt, -2, "workspace");
|
|
||||||
|
|
||||||
lua_pushlightuserdata(Lt, scriptContext.get());
|
|
||||||
lua_pushcclosure(Lt, script_wait, 1);
|
|
||||||
lua_setfield(Lt, -2, "wait");
|
|
||||||
|
|
||||||
lua_pushlightuserdata(Lt, scriptContext.get());
|
|
||||||
lua_pushcclosure(Lt, script_delay, 1);
|
|
||||||
lua_setfield(Lt, -2, "delay");
|
|
||||||
|
|
||||||
lua_pop(Lt, 1); // _G
|
|
||||||
|
|
||||||
// Push wrapper as thread function
|
// Push wrapper as thread function
|
||||||
luaL_loadbuffer(Lt, WRAPPER_SRC, strlen(WRAPPER_SRC), "=PCALL_WRAPPER");
|
lua_getfield(Lt, LUA_REGISTRYINDEX, "LuaPCallWrapper");
|
||||||
|
|
||||||
// Load source code and push onto thread as upvalue for wrapper
|
// Load source code and push onto thread as upvalue for wrapper
|
||||||
int status = luaL_loadbuffer(Lt, source.c_str(), source.size(), this->GetFullName().c_str());
|
int status = luaL_loadbuffer(Lt, source.c_str(), source.size(), this->GetFullName().c_str());
|
||||||
|
@ -71,6 +45,21 @@ void Script::Run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize script globals
|
||||||
|
scriptContext->NewEnvironment(Lt); // Pushes envtable, metatable
|
||||||
|
|
||||||
|
// Set script in metatable source
|
||||||
|
InstanceRef(shared_from_this()).PushLuaValue(Lt);
|
||||||
|
lua_setfield(Lt, -2, "source");
|
||||||
|
|
||||||
|
lua_pop(Lt, 1); // Pop metatable
|
||||||
|
|
||||||
|
// Set script in environment
|
||||||
|
InstanceRef(shared_from_this()).PushLuaValue(Lt);
|
||||||
|
lua_setfield(Lt, -2, "script");
|
||||||
|
|
||||||
|
lua_setfenv(Lt, -2); // Set env of loaded function
|
||||||
|
|
||||||
// Push our error handler and then generate the wrapped function
|
// Push our error handler and then generate the wrapped function
|
||||||
lua_pushcfunction(Lt, script_errhandler);
|
lua_pushcfunction(Lt, script_errhandler);
|
||||||
lua_call(Lt, 2, 1);
|
lua_call(Lt, 2, 1);
|
||||||
|
@ -86,33 +75,32 @@ void Script::Stop() {
|
||||||
// TODO:
|
// TODO:
|
||||||
}
|
}
|
||||||
|
|
||||||
int script_wait(lua_State* L) {
|
static std::shared_ptr<Script> getfsource(lua_State* L, lua_Debug* dbg) {
|
||||||
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
int top = lua_gettop(L);
|
||||||
float secs = lua_gettop(L) == 0 ? 0.03 : std::max(luaL_checknumber(L, 1), 0.03);
|
|
||||||
if (lua_gettop(L) > 0) lua_pop(L, 1); // pop secs
|
|
||||||
|
|
||||||
scriptContext->PushThreadSleep(L, secs);
|
lua_getinfo(L, "f", dbg);
|
||||||
|
lua_getfenv(L, -1); // Get fenv of stack pos
|
||||||
// Yield
|
if (lua_isnil(L, -1)) { // No env could be found
|
||||||
return lua_yield(L, 0);
|
lua_settop(L, top);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int script_delay(lua_State* L) {
|
// Get source from metatable
|
||||||
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
lua_getmetatable(L, -1);
|
||||||
float secs = std::max(luaL_checknumber(L, 1), 0.03);
|
lua_getfield(L, -1, "source");
|
||||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
|
||||||
|
|
||||||
lua_State* Lt = lua_newthread(L); // Create a new thread
|
auto result = InstanceRef::FromLuaValue(L, -1);
|
||||||
// I think this is memory abuse??
|
if (!result) {
|
||||||
// Wouldn't popping the thread in this case make it eligible for garbage collection?
|
lua_settop(L, top);
|
||||||
lua_pop(L, 1); // pop the newly created thread so that xmove moves func instead of it into itself
|
return nullptr;
|
||||||
lua_xmove(L, Lt, 1); // move func
|
}
|
||||||
lua_pop(L, 1); // pop secs
|
|
||||||
|
|
||||||
// Schedule next run
|
lua_settop(L, top);
|
||||||
scriptContext->PushThreadSleep(Lt, secs);
|
|
||||||
|
|
||||||
return 0;
|
std::shared_ptr<Instance> ref = result.expect().get<InstanceRef>();
|
||||||
|
if (!ref->IsA<Script>()) return nullptr;
|
||||||
|
|
||||||
|
return ref->CastTo<Script>().expect();
|
||||||
}
|
}
|
||||||
|
|
||||||
int script_errhandler(lua_State* L) {
|
int script_errhandler(lua_State* L) {
|
||||||
|
@ -121,7 +109,7 @@ int script_errhandler(lua_State* L) {
|
||||||
|
|
||||||
// Traceback
|
// Traceback
|
||||||
|
|
||||||
Logger::traceStart();
|
Logger::trace("Stack start");
|
||||||
|
|
||||||
lua_Debug dbg;
|
lua_Debug dbg;
|
||||||
int stack = 1;
|
int stack = 1;
|
||||||
|
@ -131,10 +119,13 @@ int script_errhandler(lua_State* L) {
|
||||||
if (strcmp(dbg.what, "C") == 0 || strcmp(dbg.source, "=PCALL_WRAPPER") == 0)
|
if (strcmp(dbg.what, "C") == 0 || strcmp(dbg.source, "=PCALL_WRAPPER") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Logger::trace(dbg.source, dbg.currentline);
|
// Find script source
|
||||||
|
std::shared_ptr<Script> source = getfsource(L, &dbg);
|
||||||
|
|
||||||
|
Logger::scriptLogf("'%s', Line %d", Logger::LogLevel::TRACE, {source, dbg.currentline}, dbg.source, dbg.currentline);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::traceEnd();
|
Logger::trace("Stack end");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -1,22 +1,30 @@
|
||||||
#include "scriptcontext.h"
|
#include "scriptcontext.h"
|
||||||
#include "datatypes/cframe.h"
|
#include "datatypes/cframe.h"
|
||||||
#include "datatypes/color3.h"
|
#include "datatypes/color3.h"
|
||||||
|
#include "datatypes/ref.h"
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "objects/datamodel.h"
|
||||||
|
#include "objects/service/workspace.h"
|
||||||
#include "timeutil.h"
|
#include "timeutil.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "luaapis.h" // IWYU pragma: keep
|
#include "luaapis.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
const char* WRAPPER_SRC = "local func, errhandler = ... return function(...) local args = {...} xpcall(function() func(unpack(args)) end, errhandler) end";
|
||||||
|
|
||||||
|
int g_wait(lua_State*);
|
||||||
|
int g_delay(lua_State*);
|
||||||
static int g_print(lua_State*);
|
static int g_print(lua_State*);
|
||||||
static int g_require(lua_State*);
|
static int g_require(lua_State*);
|
||||||
static const struct luaL_Reg luaglobals [] = {
|
static const luaL_Reg luaglobals [] = {
|
||||||
{"print", g_print},
|
{"print", g_print},
|
||||||
{"require", g_require},
|
{"require", g_require},
|
||||||
{NULL, NULL} /* end of array */
|
{NULL, NULL} /* end of array */
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string unsafe_globals[] = {
|
std::string unsafe_globals[] = {
|
||||||
|
// Todo implement our own "safe" setfenv/getfenv
|
||||||
"loadfile", "loadstring", "load", "dofile", "getfenv", "setfenv"
|
"loadfile", "loadstring", "load", "dofile", "getfenv", "setfenv"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,6 +56,29 @@ void ScriptContext::InitService() {
|
||||||
Color3::PushLuaLibrary(state);
|
Color3::PushLuaLibrary(state);
|
||||||
Instance::PushLuaLibrary(state);
|
Instance::PushLuaLibrary(state);
|
||||||
|
|
||||||
|
// Add other globals
|
||||||
|
lua_getglobal(state, "_G");
|
||||||
|
|
||||||
|
InstanceRef(dataModel().value()).PushLuaValue(state);
|
||||||
|
lua_setfield(state, -2, "game");
|
||||||
|
|
||||||
|
InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(state);
|
||||||
|
lua_setfield(state, -2, "workspace");
|
||||||
|
|
||||||
|
lua_pushlightuserdata(state, this);
|
||||||
|
lua_pushcclosure(state, g_wait, 1);
|
||||||
|
lua_setfield(state, -2, "wait");
|
||||||
|
|
||||||
|
lua_pushlightuserdata(state, this);
|
||||||
|
lua_pushcclosure(state, g_delay, 1);
|
||||||
|
lua_setfield(state, -2, "delay");
|
||||||
|
|
||||||
|
lua_pop(state, 1); // _G
|
||||||
|
|
||||||
|
// Add wrapper function
|
||||||
|
luaL_loadbuffer(state, WRAPPER_SRC, strlen(WRAPPER_SRC), "=PCALL_WRAPPER");
|
||||||
|
lua_setfield(state, LUA_REGISTRYINDEX, "LuaPCallWrapper");
|
||||||
|
|
||||||
// TODO: custom os library
|
// TODO: custom os library
|
||||||
|
|
||||||
// Override print
|
// Override print
|
||||||
|
@ -131,6 +162,27 @@ void ScriptContext::RunSleepingThreads() {
|
||||||
schedTime = tu_clock_micros() - startTime;
|
schedTime = tu_clock_micros() - startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptContext::NewEnvironment(lua_State* L) {
|
||||||
|
lua_newtable(L); // Env table
|
||||||
|
lua_newtable(L); // Metatable
|
||||||
|
|
||||||
|
// Push __index
|
||||||
|
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||||
|
lua_setfield(L, -2, "__index");
|
||||||
|
|
||||||
|
// Push __metatable
|
||||||
|
lua_pushstring(L, "metatable is locked");
|
||||||
|
lua_setfield(L, -2, "__metatable");
|
||||||
|
|
||||||
|
// Copy metatable and set the env table's metatable
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setmetatable(L, -3);
|
||||||
|
|
||||||
|
// Remainder on stack:
|
||||||
|
// 1. Env table
|
||||||
|
// 2. Metatable
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.lua.org/source/5.1/lbaselib.c.html
|
// https://www.lua.org/source/5.1/lbaselib.c.html
|
||||||
static int g_print(lua_State* L) {
|
static int g_print(lua_State* L) {
|
||||||
std::string buf;
|
std::string buf;
|
||||||
|
@ -162,3 +214,32 @@ static int g_require(lua_State* L) {
|
||||||
|
|
||||||
return luaL_error(L, "require is not yet implemented");
|
return luaL_error(L, "require is not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int g_wait(lua_State* L) {
|
||||||
|
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
float secs = lua_gettop(L) == 0 ? 0.03 : std::max(luaL_checknumber(L, 1), 0.03);
|
||||||
|
if (lua_gettop(L) > 0) lua_pop(L, 1); // pop secs
|
||||||
|
|
||||||
|
scriptContext->PushThreadSleep(L, secs);
|
||||||
|
|
||||||
|
// Yield
|
||||||
|
return lua_yield(L, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int g_delay(lua_State* L) {
|
||||||
|
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
|
||||||
|
float secs = std::max(luaL_checknumber(L, 1), 0.03);
|
||||||
|
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||||
|
|
||||||
|
lua_State* Lt = lua_newthread(L); // Create a new thread
|
||||||
|
// I think this is memory abuse??
|
||||||
|
// Wouldn't popping the thread in this case make it eligible for garbage collection?
|
||||||
|
lua_pop(L, 1); // pop the newly created thread so that xmove moves func instead of it into itself
|
||||||
|
lua_xmove(L, Lt, 1); // move func
|
||||||
|
lua_pop(L, 1); // pop secs
|
||||||
|
|
||||||
|
// Schedule next run
|
||||||
|
scriptContext->PushThreadSleep(Lt, secs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -32,5 +32,8 @@ public:
|
||||||
void PushThreadSleep(lua_State* thread, float delay);
|
void PushThreadSleep(lua_State* thread, float delay);
|
||||||
void RunSleepingThreads();
|
void RunSleepingThreads();
|
||||||
|
|
||||||
|
// Generates an environment with a metatable and pushes it both the env table and metatable in order onto the stack
|
||||||
|
void NewEnvironment(lua_State* state);
|
||||||
|
|
||||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<ScriptContext>(); };
|
static inline std::shared_ptr<Instance> Create() { return std::make_shared<ScriptContext>(); };
|
||||||
};
|
};
|
|
@ -11,6 +11,8 @@ extern Texture* debugFontTexture;
|
||||||
extern Shader* debugFontShader;
|
extern Shader* debugFontShader;
|
||||||
extern Shader* identityShader;
|
extern Shader* identityShader;
|
||||||
|
|
||||||
|
void drawRect(int x, int y, int width, int height, glm::vec4 color);
|
||||||
|
|
||||||
void drawChar(char c, int x, int y, float scale=1.f) {
|
void drawChar(char c, int x, int y, float scale=1.f) {
|
||||||
debugFontShader->use();
|
debugFontShader->use();
|
||||||
debugFontTexture->activate(1);
|
debugFontTexture->activate(1);
|
||||||
|
@ -41,22 +43,6 @@ void drawString(std::string str, int x, int y, float scale=1.f) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawRect(int x, int y, int w, int h, glm::vec4 color) {
|
|
||||||
identityShader->use();
|
|
||||||
identityShader->set("aColor", color);
|
|
||||||
|
|
||||||
float x0 = 2*float(x)/viewportWidth-1, y0 = 2*float(y)/viewportHeight-1, x1 = 2*float(x + w)/viewportWidth-1, y1 = 2*float(y + h)/viewportHeight-1;
|
|
||||||
float tmp;
|
|
||||||
tmp = -y0, y0 = -y1, y1 = tmp;
|
|
||||||
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glVertex3f(x0, y0, 0);
|
|
||||||
glVertex3f(x1, y0, 0);
|
|
||||||
glVertex3f(x1, y1, 0);
|
|
||||||
glVertex3f(x0, y1, 0);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
static tu_time_t lastTime;
|
static tu_time_t lastTime;
|
||||||
extern tu_time_t renderTime;
|
extern tu_time_t renderTime;
|
||||||
extern tu_time_t physTime;
|
extern tu_time_t physTime;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#include FT_FREETYPE_H
|
#include FT_FREETYPE_H
|
||||||
|
#include FT_STROKER_H
|
||||||
|
|
||||||
// https://learnopengl.com/In-Practice/Text-Rendering
|
// https://learnopengl.com/In-Practice/Text-Rendering
|
||||||
|
|
||||||
|
@ -52,6 +53,34 @@ void fontFinish() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void loadCharTexture(FT_Face& face, FT_BitmapGlyph& glyph_bitmap, Character& character) {
|
||||||
|
// Generate texture
|
||||||
|
unsigned int texture;
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
glTexImage2D(
|
||||||
|
GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_RED,
|
||||||
|
glyph_bitmap->bitmap.width,
|
||||||
|
glyph_bitmap->bitmap.rows,
|
||||||
|
0,
|
||||||
|
GL_RED,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
glyph_bitmap->bitmap.buffer
|
||||||
|
);
|
||||||
|
// set texture options
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
// now store character for later use
|
||||||
|
character.ID = texture;
|
||||||
|
character.size = glm::ivec2(glyph_bitmap->bitmap.width, glyph_bitmap->bitmap.rows);
|
||||||
|
character.bearing = glm::ivec2(glyph_bitmap->left, glyph_bitmap->top);
|
||||||
|
character.advance = (unsigned int)face->glyph->advance.x;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Font> loadFont(std::string fontName) {
|
std::shared_ptr<Font> loadFont(std::string fontName) {
|
||||||
std::string fontPath = "assets/font/" + fontName;
|
std::string fontPath = "assets/font/" + fontName;
|
||||||
|
|
||||||
|
@ -63,52 +92,56 @@ std::shared_ptr<Font> loadFont(std::string fontName) {
|
||||||
|
|
||||||
std::shared_ptr<Font> font = std::make_shared<Font>();
|
std::shared_ptr<Font> font = std::make_shared<Font>();
|
||||||
|
|
||||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
FT_Set_Pixel_Sizes(face, 0, 16);
|
||||||
|
font->height = face->size->metrics.y_ppem;
|
||||||
|
|
||||||
|
FT_Stroker stroker;
|
||||||
|
FT_Stroker_New(freetype, &stroker);
|
||||||
|
FT_Stroker_Set(stroker, 2 * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
|
||||||
|
|
||||||
// Load each glyph
|
// Load each glyph
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
for (unsigned char c = 0; c < 128; c++) {
|
for (unsigned char c = 0; c < 128; c++) {
|
||||||
// load character glyph
|
// load character glyph
|
||||||
if (FT_Error err = FT_Load_Char(face, c, FT_LOAD_RENDER)) {
|
if (FT_Error err = FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
|
||||||
Logger::errorf("Failed to load glyph %d in font '%s': [%d]", c, fontName.c_str(), err);
|
Logger::errorf("Failed to load glyph %d in font '%s': [%d]", c, fontName.c_str(), err);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate texture
|
FT_Glyph glyph;
|
||||||
unsigned int texture;
|
FT_BitmapGlyph glyph_bitmap;
|
||||||
glGenTextures(1, &texture);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
|
||||||
glTexImage2D(
|
|
||||||
GL_TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
GL_RED,
|
|
||||||
face->glyph->bitmap.width,
|
|
||||||
face->glyph->bitmap.rows,
|
|
||||||
0,
|
|
||||||
GL_RED,
|
|
||||||
GL_UNSIGNED_BYTE,
|
|
||||||
face->glyph->bitmap.buffer
|
|
||||||
);
|
|
||||||
// set texture options
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
// now store character for later use
|
|
||||||
Character character;
|
Character character;
|
||||||
character.ID = texture;
|
|
||||||
character.size = glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows);
|
// Render base
|
||||||
character.bearing = glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top);
|
FT_Get_Glyph(face->glyph, &glyph);
|
||||||
character.advance = (unsigned int)face->glyph->advance.x;
|
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, true);
|
||||||
|
glyph_bitmap = (FT_BitmapGlyph)glyph;
|
||||||
|
|
||||||
|
loadCharTexture(face, glyph_bitmap, character);
|
||||||
font->characters[c] = character;
|
font->characters[c] = character;
|
||||||
|
FT_Done_Glyph(glyph);
|
||||||
|
// TODO: Find out how to clear FT_BitmapGlyph... I cant import FT_Bitmap_Done for some reason
|
||||||
|
|
||||||
|
// Render stroked
|
||||||
|
// https://stackoverflow.com/a/28078293/16255372
|
||||||
|
FT_Get_Glyph(face->glyph, &glyph);
|
||||||
|
FT_Glyph_StrokeBorder(&glyph, stroker, false, true);
|
||||||
|
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, true);
|
||||||
|
glyph_bitmap = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
||||||
|
|
||||||
|
loadCharTexture(face, glyph_bitmap, character);
|
||||||
|
font->strokeCharacters[c] = character;
|
||||||
|
FT_Done_Glyph(glyph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FT_Stroker_Done(stroker);
|
||||||
FT_Done_Face(face);
|
FT_Done_Face(face);
|
||||||
|
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale, glm::vec3 color) {
|
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale, glm::vec3 color, bool drawStroke) {
|
||||||
// activate corresponding render state
|
// activate corresponding render state
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
@ -128,10 +161,10 @@ void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, fl
|
||||||
// iterate through all characters
|
// iterate through all characters
|
||||||
for (size_t i = 0; i < text.size(); i++) {
|
for (size_t i = 0; i < text.size(); i++) {
|
||||||
unsigned char c = text[i];
|
unsigned char c = text[i];
|
||||||
Character ch = font->characters[c];
|
Character ch = drawStroke ? font->strokeCharacters[c] : font->characters[c];
|
||||||
|
|
||||||
float xpos = x + ch.bearing.x * scale;
|
float xpos = x + ch.bearing.x * scale;
|
||||||
float ypos = y - (ch.size.y - ch.bearing.y) * scale;
|
float ypos = viewportHeight - y - font->height - (ch.size.y - ch.bearing.y) * scale;
|
||||||
|
|
||||||
float w = ch.size.x * scale;
|
float w = ch.size.x * scale;
|
||||||
float h = ch.size.y * scale;
|
float h = ch.size.y * scale;
|
||||||
|
@ -160,3 +193,16 @@ void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, fl
|
||||||
}
|
}
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float calcTextWidth(std::shared_ptr<Font> font, std::string text, float scale) {
|
||||||
|
float x = 0;
|
||||||
|
// iterate through all characters
|
||||||
|
for (size_t i = 0; i < text.size(); i++) {
|
||||||
|
unsigned char c = text[i];
|
||||||
|
Character ch = font->characters[c];
|
||||||
|
|
||||||
|
x += (ch.advance >> 6) * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
|
@ -14,10 +14,13 @@ struct Character {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Font {
|
struct Font {
|
||||||
|
unsigned int height;
|
||||||
Character characters[128];
|
Character characters[128];
|
||||||
|
Character strokeCharacters[128];
|
||||||
};
|
};
|
||||||
|
|
||||||
void fontInit();
|
void fontInit();
|
||||||
void fontFinish();
|
void fontFinish();
|
||||||
std::shared_ptr<Font> loadFont(std::string fontName);
|
std::shared_ptr<Font> loadFont(std::string fontName);
|
||||||
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale=1.f, glm::vec3 color = glm::vec3(1,1,1));
|
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale=1.f, glm::vec3 color = glm::vec3(1,1,1), bool drawStroke = false);
|
||||||
|
float calcTextWidth(std::shared_ptr<Font> font, std::string text, float scale = 1.f);
|
|
@ -20,6 +20,8 @@
|
||||||
#include "datatypes/vector.h"
|
#include "datatypes/vector.h"
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
#include "math_helper.h"
|
#include "math_helper.h"
|
||||||
|
#include "objects/hint.h"
|
||||||
|
#include "objects/message.h"
|
||||||
#include "objects/service/selection.h"
|
#include "objects/service/selection.h"
|
||||||
#include "partassembly.h"
|
#include "partassembly.h"
|
||||||
#include "rendering/font.h"
|
#include "rendering/font.h"
|
||||||
|
@ -62,7 +64,8 @@ bool wireframeRendering = false;
|
||||||
int viewportWidth, viewportHeight;
|
int viewportWidth, viewportHeight;
|
||||||
|
|
||||||
void renderDebugInfo();
|
void renderDebugInfo();
|
||||||
void drawRect(int x, int y, int width, int height, glm::vec3 color);
|
void drawRect(int x, int y, int width, int height, glm::vec4 color);
|
||||||
|
inline void drawRect(int x, int y, int width, int height, glm::vec3 color) { return drawRect(x, y, width, height, glm::vec4(color, 1)); };
|
||||||
|
|
||||||
void renderInit(int width, int height) {
|
void renderInit(int width, int height) {
|
||||||
viewportWidth = width, viewportHeight = height;
|
viewportWidth = width, viewportHeight = height;
|
||||||
|
@ -649,6 +652,33 @@ void addDebugRenderCFrame(CFrame frame, Color3 color) {
|
||||||
DEBUG_CFRAMES.push_back(std::make_pair(frame, color));
|
DEBUG_CFRAMES.push_back(std::make_pair(frame, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderMessages() {
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
// glEnable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
// glEnable(GL_BLEND);
|
||||||
|
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
|
||||||
|
if (!it->IsA<Message>()) continue;
|
||||||
|
std::shared_ptr<Message> message = it->CastTo<Message>().expect();
|
||||||
|
|
||||||
|
float textWidth = calcTextWidth(sansSerif, message->text);
|
||||||
|
|
||||||
|
// Render hint
|
||||||
|
if (message->GetClass() == &Hint::TYPE) {
|
||||||
|
drawRect(0, 0, viewportWidth, 20, glm::vec4(0,0,0,1));
|
||||||
|
drawText(sansSerif, message->text, (viewportWidth - textWidth) / 2, 0);
|
||||||
|
} else {
|
||||||
|
// Don't draw if text is empty
|
||||||
|
if (message->text == "") continue;
|
||||||
|
|
||||||
|
drawRect(0, 0, viewportWidth, viewportHeight, glm::vec4(0.5));
|
||||||
|
drawText(sansSerif, message->text, ((float)viewportWidth - textWidth) / 2, ((float)viewportHeight - sansSerif->height) / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tu_time_t renderTime;
|
tu_time_t renderTime;
|
||||||
void render() {
|
void render() {
|
||||||
tu_time_t startTime = tu_clock_micros();
|
tu_time_t startTime = tu_clock_micros();
|
||||||
|
@ -668,13 +698,14 @@ void render() {
|
||||||
renderWireframe();
|
renderWireframe();
|
||||||
if (debugRendererEnabled)
|
if (debugRendererEnabled)
|
||||||
renderDebugInfo();
|
renderDebugInfo();
|
||||||
|
renderMessages();
|
||||||
// TODO: Make this a debug flag
|
// TODO: Make this a debug flag
|
||||||
// renderAABB();
|
// renderAABB();
|
||||||
|
|
||||||
renderTime = tu_clock_micros() - startTime;
|
renderTime = tu_clock_micros() - startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawRect(int x, int y, int width, int height, glm::vec3 color) {
|
void drawRect(int x, int y, int width, int height, glm::vec4 color) {
|
||||||
// GL_CULL_FACE has to be disabled as we are flipping the order of the vertices here, besides we don't really care about it
|
// GL_CULL_FACE has to be disabled as we are flipping the order of the vertices here, besides we don't really care about it
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
glm::mat4 model(1.0f); // Same applies to this VV
|
glm::mat4 model(1.0f); // Same applies to this VV
|
||||||
|
|
|
@ -38,6 +38,8 @@ set(PROJECT_SOURCES
|
||||||
panes/outputtextview.cpp
|
panes/outputtextview.cpp
|
||||||
script/scriptdocument.h
|
script/scriptdocument.h
|
||||||
script/scriptdocument.cpp
|
script/scriptdocument.cpp
|
||||||
|
script/commandedit.h
|
||||||
|
script/commandedit.cpp
|
||||||
aboutdialog.ui
|
aboutdialog.ui
|
||||||
aboutdialog.h
|
aboutdialog.h
|
||||||
aboutdialog.cpp
|
aboutdialog.cpp
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "./ui_mainwindow.h"
|
#include "./ui_mainwindow.h"
|
||||||
|
#include "script/commandedit.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "aboutdialog.h"
|
#include "aboutdialog.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
#include <qevent.h>
|
#include <qevent.h>
|
||||||
#include <qglobal.h>
|
#include <qglobal.h>
|
||||||
#include <qkeysequence.h>
|
#include <qkeysequence.h>
|
||||||
|
#include <qlabel.h>
|
||||||
#include <qmessagebox.h>
|
#include <qmessagebox.h>
|
||||||
#include <qmimedata.h>
|
#include <qmimedata.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
|
@ -117,6 +119,8 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
undoManager.SetUndoStateListener([&]() {
|
undoManager.SetUndoStateListener([&]() {
|
||||||
updateToolbars();
|
updateToolbars();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setUpCommandBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent* evt) {
|
void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||||
|
@ -142,6 +146,14 @@ void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::setUpCommandBar() {
|
||||||
|
CommandEdit* commandEdit;
|
||||||
|
QToolBar* commandBar = ui->commandBar;
|
||||||
|
commandBar->layout()->setSpacing(5);
|
||||||
|
commandBar->addWidget(new QLabel(tr("Command ")));
|
||||||
|
commandBar->addWidget(commandEdit = new CommandEdit());
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::connectActionHandlers() {
|
void MainWindow::connectActionHandlers() {
|
||||||
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = TOOL_SELECT; updateToolbars(); });
|
||||||
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_MOVE : TOOL_SELECT; updateToolbars(); });
|
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? TOOL_MOVE : TOOL_SELECT; updateToolbars(); });
|
||||||
|
@ -598,19 +610,23 @@ ScriptDocument* MainWindow::findScriptWindow(std::shared_ptr<Script> script) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openScriptDocument(std::shared_ptr<Script> script) {
|
void MainWindow::openScriptDocument(std::shared_ptr<Script> script, int line) {
|
||||||
// Document already exists, don't open it
|
// Document already exists, don't open it
|
||||||
ScriptDocument* doc = findScriptWindow(script);
|
ScriptDocument* doc = findScriptWindow(script);
|
||||||
if (doc != nullptr) {
|
if (doc != nullptr) {
|
||||||
ui->mdiArea->setActiveSubWindow(doc);
|
ui->mdiArea->setActiveSubWindow(doc);
|
||||||
|
doc->setFocus();
|
||||||
|
if (line > -1) doc->moveCursor(line);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
doc = new ScriptDocument(script);
|
doc = new ScriptDocument(script);
|
||||||
doc->setAttribute(Qt::WA_DeleteOnClose, true);
|
doc->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
|
if (line > -1) doc->moveCursor(line);
|
||||||
ui->mdiArea->addSubWindow(doc);
|
ui->mdiArea->addSubWindow(doc);
|
||||||
ui->mdiArea->setActiveSubWindow(doc);
|
ui->mdiArea->setActiveSubWindow(doc);
|
||||||
doc->showMaximized();
|
doc->showMaximized();
|
||||||
|
doc->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::closeScriptDocument(std::shared_ptr<Script> script) {
|
void MainWindow::closeScriptDocument(std::shared_ptr<Script> script) {
|
||||||
|
|
|
@ -58,7 +58,7 @@ public:
|
||||||
GridSnappingMode snappingMode;
|
GridSnappingMode snappingMode;
|
||||||
bool editSoundEffects = true;
|
bool editSoundEffects = true;
|
||||||
|
|
||||||
void openScriptDocument(std::shared_ptr<Script>);
|
void openScriptDocument(std::shared_ptr<Script>, int line = -1);
|
||||||
void closeScriptDocument(std::shared_ptr<Script>);
|
void closeScriptDocument(std::shared_ptr<Script>);
|
||||||
|
|
||||||
void openFile(std::string path);
|
void openFile(std::string path);
|
||||||
|
@ -69,12 +69,12 @@ public:
|
||||||
private:
|
private:
|
||||||
PlaceDocument* placeDocument;
|
PlaceDocument* placeDocument;
|
||||||
|
|
||||||
|
void setUpCommandBar();
|
||||||
|
void connectActionHandlers();
|
||||||
void updateToolbars();
|
void updateToolbars();
|
||||||
void closeEvent(QCloseEvent* evt) override;
|
void closeEvent(QCloseEvent* evt) override;
|
||||||
ScriptDocument* findScriptWindow(std::shared_ptr<Script>);
|
ScriptDocument* findScriptWindow(std::shared_ptr<Script>);
|
||||||
|
|
||||||
void connectActionHandlers();
|
|
||||||
|
|
||||||
std::optional<std::string> openFileDialog(QString filter, QString defaultExtension, QFileDialog::AcceptMode acceptMode, QString title = "");
|
std::optional<std::string> openFileDialog(QString filter, QString defaultExtension, QFileDialog::AcceptMode acceptMode, QString title = "");
|
||||||
};
|
};
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>MainWindow</string>
|
<string>Openblocks Editor</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QToolBar" name="editTools">
|
<widget class="QToolBar" name="editTools">
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>toolBar_3</string>
|
<string>Edit Tools</string>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="toolBarArea">
|
<attribute name="toolBarArea">
|
||||||
<enum>TopToolBarArea</enum>
|
<enum>TopToolBarArea</enum>
|
||||||
|
@ -237,7 +237,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QToolBar" name="toolBar">
|
<widget class="QToolBar" name="toolBar">
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>toolBar</string>
|
<string>Sound Controls</string>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="toolBarArea">
|
<attribute name="toolBarArea">
|
||||||
<enum>TopToolBarArea</enum>
|
<enum>TopToolBarArea</enum>
|
||||||
|
@ -247,6 +247,17 @@
|
||||||
</attribute>
|
</attribute>
|
||||||
<addaction name="actionToggleEditSounds"/>
|
<addaction name="actionToggleEditSounds"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QToolBar" name="commandBar">
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Command Bar</string>
|
||||||
|
</property>
|
||||||
|
<attribute name="toolBarArea">
|
||||||
|
<enum>BottomToolBarArea</enum>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="toolBarBreak">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
<action name="actionAddPart">
|
<action name="actionAddPart">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset>
|
<iconset>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "outputtextview.h"
|
#include "outputtextview.h"
|
||||||
|
#include "logger.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "objects/script.h"
|
|
||||||
#include "panes/outputtextview.h"
|
#include "panes/outputtextview.h"
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
|
@ -12,8 +12,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
OutputTextView::OutputTextView(QWidget* parent) : QTextEdit(parent) {
|
OutputTextView::OutputTextView(QWidget* parent) : QTextEdit(parent) {
|
||||||
Logger::addLogListener(std::bind(&OutputTextView::handleLog, this, std::placeholders::_1, std::placeholders::_2));
|
Logger::addLogListener(std::bind(&OutputTextView::handleLog, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||||
Logger::addLogListener(std::bind(&OutputTextView::handleLogTrace, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
|
||||||
ensureCursorVisible();
|
ensureCursorVisible();
|
||||||
|
|
||||||
QFont font("");
|
QFont font("");
|
||||||
|
@ -36,13 +35,9 @@ OutputTextView::OutputTextView(QWidget* parent) : QTextEdit(parent) {
|
||||||
|
|
||||||
OutputTextView::~OutputTextView() = default;
|
OutputTextView::~OutputTextView() = default;
|
||||||
|
|
||||||
void OutputTextView::handleLog(Logger::LogLevel logLevel, std::string message) {
|
void OutputTextView::handleLog(Logger::LogLevel logLevel, std::string message, Logger::ScriptSource source) {
|
||||||
if (logLevel == Logger::LogLevel::DEBUG) return;
|
if (logLevel == Logger::LogLevel::DEBUG) return;
|
||||||
|
|
||||||
// Skip if trace, as that is handled by handleLogTrace
|
|
||||||
if (logLevel == Logger::LogLevel::TRACE && !message.starts_with("Stack"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/61722734/16255372
|
// https://stackoverflow.com/a/61722734/16255372
|
||||||
moveCursor(QTextCursor::MoveOperation::End);
|
moveCursor(QTextCursor::MoveOperation::End);
|
||||||
QTextCursor cursor = textCursor();
|
QTextCursor cursor = textCursor();
|
||||||
|
@ -58,24 +53,13 @@ void OutputTextView::handleLog(Logger::LogLevel logLevel, std::string message) {
|
||||||
format.setFontWeight(QFont::Bold);
|
format.setFontWeight(QFont::Bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.insertText(message.c_str(), format);
|
// Add anchor point if source is provided
|
||||||
cursor.insertText("\n", QTextCharFormat());
|
if (source.script != nullptr) {
|
||||||
}
|
|
||||||
|
|
||||||
void OutputTextView::handleLogTrace(std::string message, std::string source, int line, void* userData) {
|
|
||||||
std::weak_ptr<Script>* script = (std::weak_ptr<Script>*)userData;
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/61722734/16255372
|
|
||||||
QTextCursor cursor = textCursor();
|
|
||||||
QTextCharFormat format = cursor.charFormat();
|
|
||||||
format.setForeground(QColor(0, 127, 255));
|
|
||||||
|
|
||||||
if (userData != nullptr && !script->expired()) {
|
|
||||||
int id = stackTraceScriptsLastId++;
|
int id = stackTraceScriptsLastId++;
|
||||||
stackTraceScripts[id] = *script;
|
stackTraceScripts[id] = source.script;
|
||||||
|
|
||||||
format.setAnchor(true);
|
format.setAnchor(true);
|
||||||
format.setAnchorHref(QString::number(id));
|
format.setAnchorHref(QString::number(id) + ":" + QString::number(source.line));
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.insertText(message.c_str(), format);
|
cursor.insertText(message.c_str(), format);
|
||||||
|
@ -87,11 +71,13 @@ void OutputTextView::mousePressEvent(QMouseEvent *e) {
|
||||||
QString anchor = anchorAt(e->pos());
|
QString anchor = anchorAt(e->pos());
|
||||||
if (anchor == "" || e->modifiers() & Qt::AltModifier) return QTextEdit::mousePressEvent(e);
|
if (anchor == "" || e->modifiers() & Qt::AltModifier) return QTextEdit::mousePressEvent(e);
|
||||||
|
|
||||||
auto script = stackTraceScripts[anchor.toInt()];
|
int idx = anchor.indexOf(":");
|
||||||
|
int id = anchor.mid(0, idx).toInt(), line = anchor.mid(idx+1).toInt();
|
||||||
|
auto script = stackTraceScripts[id];
|
||||||
if (script.expired()) return QTextEdit::mousePressEvent(e);
|
if (script.expired()) return QTextEdit::mousePressEvent(e);
|
||||||
|
|
||||||
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
||||||
mainWnd->openScriptDocument(script.lock());
|
mainWnd->openScriptDocument(script.lock(), line);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputTextView::mouseReleaseEvent(QMouseEvent *e) {
|
void OutputTextView::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
|
|
@ -14,8 +14,7 @@ private:
|
||||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
void mouseMoveEvent(QMouseEvent *e) override;
|
void mouseMoveEvent(QMouseEvent *e) override;
|
||||||
|
|
||||||
void handleLog(Logger::LogLevel, std::string);
|
void handleLog(Logger::LogLevel, std::string, Logger::ScriptSource source);
|
||||||
void handleLogTrace(std::string, std::string, int, void*);
|
|
||||||
|
|
||||||
std::map<int, std::weak_ptr<Script>> stackTraceScripts;
|
std::map<int, std::weak_ptr<Script>> stackTraceScripts;
|
||||||
int stackTraceScriptsLastId = 0;
|
int stackTraceScriptsLastId = 0;
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <cfloat>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <qapplication.h>
|
#include <qapplication.h>
|
||||||
|
@ -24,6 +23,14 @@
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qtreewidget.h>
|
#include <qtreewidget.h>
|
||||||
|
|
||||||
|
QDoubleSpinBox* makeDoubleSpinBox(QWidget* parent = nullptr) {
|
||||||
|
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
|
||||||
|
spinBox->setMaximum(INFINITY);
|
||||||
|
spinBox->setMinimum(-INFINITY);
|
||||||
|
spinBox->setDecimals(4);
|
||||||
|
return spinBox;
|
||||||
|
}
|
||||||
|
|
||||||
class PropertiesItemDelegate : public QStyledItemDelegate {
|
class PropertiesItemDelegate : public QStyledItemDelegate {
|
||||||
PropertiesView* view;
|
PropertiesView* view;
|
||||||
public:
|
public:
|
||||||
|
@ -64,7 +71,7 @@ public:
|
||||||
Vector3 vector = currentValue.get<Vector3>();
|
Vector3 vector = currentValue.get<Vector3>();
|
||||||
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
float value = componentName == "X" ? vector.X() : componentName == "Y" ? vector.Y() : componentName == "Z" ? vector.Z() : 0;
|
||||||
|
|
||||||
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
|
QDoubleSpinBox* spinBox = makeDoubleSpinBox(parent);
|
||||||
spinBox->setValue(value);
|
spinBox->setValue(value);
|
||||||
|
|
||||||
return spinBox;
|
return spinBox;
|
||||||
|
@ -75,9 +82,9 @@ public:
|
||||||
|
|
||||||
if (meta.type.descriptor == &FLOAT_TYPE) {
|
if (meta.type.descriptor == &FLOAT_TYPE) {
|
||||||
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
|
QDoubleSpinBox* spinBox = new QDoubleSpinBox(parent);
|
||||||
spinBox->setValue(currentValue.get<float>());
|
|
||||||
spinBox->setMinimum(-INFINITY);
|
spinBox->setMinimum(-INFINITY);
|
||||||
spinBox->setMaximum(INFINITY);
|
spinBox->setMaximum(INFINITY);
|
||||||
|
spinBox->setValue(currentValue.get<float>());
|
||||||
|
|
||||||
if (meta.flags & PROP_UNIT_FLOAT) {
|
if (meta.flags & PROP_UNIT_FLOAT) {
|
||||||
spinBox->setMinimum(0);
|
spinBox->setMinimum(0);
|
||||||
|
@ -221,7 +228,8 @@ public:
|
||||||
: componentName == "Z" ? Vector3(prev.X(), prev.Y(), value) : prev;
|
: componentName == "Z" ? Vector3(prev.X(), prev.Y(), value) : prev;
|
||||||
|
|
||||||
inst->SetProperty(propertyName, newVector).expect();
|
inst->SetProperty(propertyName, newVector).expect();
|
||||||
view->rebuildCompositeProperty(view->itemFromIndex(index.parent()), &Vector3::TYPE, newVector);
|
// SetProperty above already causes the composite to be rebuilt. So we get rid of it here to prevent errors
|
||||||
|
// view->rebuildCompositeProperty(view->itemFromIndex(index.parent()), &Vector3::TYPE, newVector);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
115
editor/script/commandedit.cpp
Normal file
115
editor/script/commandedit.cpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#include "commandedit.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "lua.h"
|
||||||
|
#include "objects/service/script/scriptcontext.h"
|
||||||
|
#include "luaapis.h" // IWYU pragma: keep
|
||||||
|
#include <qevent.h>
|
||||||
|
#include <qlineedit.h>
|
||||||
|
#include <qnamespace.h>
|
||||||
|
|
||||||
|
int script_errhandler(lua_State*);
|
||||||
|
|
||||||
|
CommandEdit::CommandEdit(QWidget* parent) : QLineEdit(parent) {
|
||||||
|
connect(this, &QLineEdit::returnPressed, this, &CommandEdit::executeCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandEdit::~CommandEdit() = default;
|
||||||
|
|
||||||
|
void CommandEdit::executeCommand() {
|
||||||
|
std::string command = this->text().toStdString();
|
||||||
|
|
||||||
|
// Output
|
||||||
|
Logger::infof("> %s", command.c_str());
|
||||||
|
|
||||||
|
// Select all so that the user can type over it
|
||||||
|
this->selectAll();
|
||||||
|
|
||||||
|
// Execute via Lua
|
||||||
|
auto context = gDataModel->GetService<ScriptContext>();
|
||||||
|
lua_State* L = context->state;
|
||||||
|
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
lua_State* Lt = lua_newthread(L);
|
||||||
|
|
||||||
|
// Push wrapper as thread function
|
||||||
|
lua_getfield(Lt, LUA_REGISTRYINDEX, "LuaPCallWrapper");
|
||||||
|
|
||||||
|
// Load source code and push onto thread as upvalue for wrapper
|
||||||
|
int status = luaL_loadstring(Lt, command.c_str());
|
||||||
|
if (status != LUA_OK) {
|
||||||
|
// Failed to parse/load chunk
|
||||||
|
Logger::error(lua_tostring(Lt, -1));
|
||||||
|
|
||||||
|
lua_settop(L, top);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrCreateEnvironment(Lt);
|
||||||
|
lua_setfenv(Lt, -2); // Set env of loaded function
|
||||||
|
|
||||||
|
// Push our error handler and then generate the wrapped function
|
||||||
|
lua_pushcfunction(Lt, script_errhandler);
|
||||||
|
lua_call(Lt, 2, 1);
|
||||||
|
|
||||||
|
|
||||||
|
// Resume the thread
|
||||||
|
lua_resume(Lt, 0);
|
||||||
|
|
||||||
|
lua_pop(L, 1); // Pop the thread
|
||||||
|
lua_settop(L, top);
|
||||||
|
|
||||||
|
// Push to history
|
||||||
|
if (commandHistory.size() == 0 || commandHistory.back() != command) {
|
||||||
|
historyIndex = commandHistory.size();
|
||||||
|
commandHistory.push_back(command);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Gets the command bar environment from the registry, or creates a new one and registers it
|
||||||
|
void CommandEdit::getOrCreateEnvironment(lua_State* L) {
|
||||||
|
auto context = gDataModel->GetService<ScriptContext>();
|
||||||
|
|
||||||
|
// Try to find existing environment
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, "commandBarEnv");
|
||||||
|
if (!lua_isnil(L, -1))
|
||||||
|
return; // Return the found environment
|
||||||
|
lua_pop(L, 1); // Pop nil
|
||||||
|
|
||||||
|
// Initialize script globals
|
||||||
|
context->NewEnvironment(L); // Pushes envtable, metatable
|
||||||
|
|
||||||
|
// Set source in metatable
|
||||||
|
lua_pushstring(L, "commandbar");
|
||||||
|
lua_setfield(L, -2, "source");
|
||||||
|
|
||||||
|
lua_pop(L, 1); // Pop metatable
|
||||||
|
|
||||||
|
// Register it
|
||||||
|
lua_pushvalue(L, -1); // Copy
|
||||||
|
lua_setfield(L, LUA_REGISTRYINDEX, "commandBarEnv");
|
||||||
|
|
||||||
|
// Remainder on stack:
|
||||||
|
// 1. Env table
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandEdit::keyPressEvent(QKeyEvent* evt) {
|
||||||
|
switch (evt->key()) {
|
||||||
|
case Qt::Key_Up:
|
||||||
|
if (historyIndex > 0)
|
||||||
|
historyIndex--;
|
||||||
|
|
||||||
|
if (commandHistory.size() > 0)
|
||||||
|
this->setText(QString::fromStdString(commandHistory[historyIndex]));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Down:
|
||||||
|
if (historyIndex+1 < (int)commandHistory.size())
|
||||||
|
historyIndex++;
|
||||||
|
|
||||||
|
if (commandHistory.size() > 0)
|
||||||
|
this->setText(QString::fromStdString(commandHistory[historyIndex]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QLineEdit::keyPressEvent(evt);
|
||||||
|
}
|
21
editor/script/commandedit.h
Normal file
21
editor/script/commandedit.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qlineedit.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct lua_State;
|
||||||
|
|
||||||
|
class CommandEdit : public QLineEdit {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
std::vector<std::string> commandHistory;
|
||||||
|
int historyIndex = 0;
|
||||||
|
|
||||||
|
void executeCommand();
|
||||||
|
void getOrCreateEnvironment(lua_State* L);
|
||||||
|
public:
|
||||||
|
CommandEdit(QWidget* parent = nullptr);
|
||||||
|
~CommandEdit();
|
||||||
|
|
||||||
|
void keyPressEvent(QKeyEvent *) override;
|
||||||
|
};
|
|
@ -179,6 +179,13 @@ ScriptDocument::ScriptDocument(std::shared_ptr<Script> script, QWidget* parent):
|
||||||
ScriptDocument::~ScriptDocument() {
|
ScriptDocument::~ScriptDocument() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptDocument::moveCursor(int line) {
|
||||||
|
if (line == -1) return;
|
||||||
|
|
||||||
|
int lineLength = scintilla->lineLength(line-1);
|
||||||
|
scintilla->setCursorPosition(line-1, lineLength-1);
|
||||||
|
}
|
||||||
|
|
||||||
QsciAPIs* makeApis(QsciLexer* lexer) {
|
QsciAPIs* makeApis(QsciLexer* lexer) {
|
||||||
QsciAPIs* apis = new QsciAPIs(lexer);
|
QsciAPIs* apis = new QsciAPIs(lexer);
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,5 @@ public:
|
||||||
~ScriptDocument() override;
|
~ScriptDocument() override;
|
||||||
|
|
||||||
inline std::shared_ptr<Script> getScript() { return script; }
|
inline std::shared_ptr<Script> getScript() { return script; }
|
||||||
|
void moveCursor(int line);
|
||||||
};
|
};
|
Loading…
Add table
Reference in a new issue