diff --git a/TODO b/TODO index 88d525e8..d99b7181 100644 --- a/TODO +++ b/TODO @@ -2,14 +2,13 @@ High priority TODO in order (before first public disclose) [bart] tab widgets -[bart] multiline rich text widget [bart] chat with tabs [bart] scrollbar [bart] scrollable widgets [bart] complete miniwindow (close, minimize, resize) [bart] move windows [bart] add top menu buttons -[bart] console history, scrolling +[bart] console scrolling [bart] modules managment interface [bart] adjust interface design @@ -34,7 +33,6 @@ Low priority TODO [bart] load modules from zip packages [bart] create a class for reading binary files [bart] rework lua/c++ logger -[bart] save lists on config manager [bart] make protocol class compatible with old tibia protocols == Graphics @@ -42,20 +40,19 @@ Low priority TODO [bart] cache renders into framebuffers [bart] use hardware buffer [bart] use indices -[bart] change mouse icon == Lua [bart] make possible to bind non LuaObject derived classes on lua engine (for usage with Point,Rect,Color,Size) -[bart] bind every global lua function in static classes [bart] review usage of x,y/width,height in lua instead of point/size == Platform [bart] port to MacOs and iphone +[bart] KeyDown, KeyText events change win32 mouse cursor icon == UI [bart] fix massive hotkeys when holding down a key -[bart] horizontal box layout +[bart] multiline rich text widget [bart] move layout proprieties to widget style [bart] multiline text editor widget [bart] create UIMessageBox, UIToolTip and UIInputBox @@ -66,7 +63,7 @@ change win32 mouse cursor icon [bart] reapply anchor styles when adding new childs [bart] ui text selection [bart] make set of background/icon/image width alone work -[bart] check for recursive anchors to print a error and avoid crashes +[bart] check for recursive anchors and print a error instead of crashing == Client modules [bart] make possible to reload modules diff --git a/modules/addon_terminal/terminal.lua b/modules/addon_terminal/terminal.lua index 43896b17..4d661b95 100644 --- a/modules/addon_terminal/terminal.lua +++ b/modules/addon_terminal/terminal.lua @@ -6,6 +6,7 @@ local LogColors = { [LogInfo] = 'white', [LogError] = 'red' } local MaxLogLines = 80 local LabelHeight = 16 +local MaxHistory = 1000 -- private variables local terminalWidget @@ -109,6 +110,8 @@ function Terminal.init() terminalButton = TopMenu.addButton('terminalButton', 'Terminal (Ctrl + T)', '/core_styles/icons/terminal.png', Terminal.toggle) Hotkeys.bind('Ctrl+T', Terminal.toggle) + commandHistory = Settings.getList('terminal-history') + commandLineEdit = terminalWidget:getChildById('commandLineEdit') Hotkeys.bind('Up', function() navigateCommand(1) end, commandLineEdit) Hotkeys.bind('Down', function() navigateCommand(-1) end, commandLineEdit) @@ -122,6 +125,7 @@ function Terminal.init() end function Terminal.terminate() + Settings.setList('terminal-history', commandHistory) Hotkeys.unbind('Ctrl+T') Logger.setOnLog(nil) terminalButton:destroy() @@ -186,6 +190,9 @@ function Terminal.executeCommand(command) -- add new command to history table.insert(commandHistory, command) + if #commandHistory > MaxHistory then + table.remove(commandHistory, 1) + end -- add command line Terminal.addLine(">> " .. command, "#ffffff") diff --git a/modules/client_topmenu/topmenu.lua b/modules/client_topmenu/topmenu.lua index d1c5de34..72406cbe 100644 --- a/modules/client_topmenu/topmenu.lua +++ b/modules/client_topmenu/topmenu.lua @@ -4,6 +4,7 @@ TopMenu = {} local topMenu local leftButtonsPanel local rightButtonsPanel +local gameButtonsPanel -- private functions local function onLogout() @@ -19,6 +20,7 @@ function TopMenu.init() topMenu = displayUI('topmenu.otui') leftButtonsPanel = topMenu:getChildById('leftButtonsPanel') rightButtonsPanel = topMenu:getChildById('rightButtonsPanel') + gameButtonsPanel = topMenu:getChildById('gameButtonsPanel') TopMenu.addRightButton('logoutButton', 'Logout (Ctrl+Q)', '/core_styles/icons/logout.png', onLogout) Hotkeys.bind('Ctrl+Q', onLogout) @@ -51,6 +53,15 @@ function TopMenu.addButton(id, description, icon, callback, right) return button end +function TopMenu.addGameButton(id, description, icon, callback) + local button = createWidget('GameTopButton', gameButtonsPanel) + button:setId(id) + button:setTooltip(description) + button:setIcon(resolvepath(icon, 2)) + button.onClick = callback + return button +end + function TopMenu.addLeftButton(id, description, icon, callback) return TopMenu.addButton(id, description, icon, callback, false) end diff --git a/modules/client_topmenu/topmenu.otui b/modules/client_topmenu/topmenu.otui index 6a9b82b3..fdd5cebe 100644 --- a/modules/client_topmenu/topmenu.otui +++ b/modules/client_topmenu/topmenu.otui @@ -1,22 +1,45 @@ TopButton < UIButton size: 26 26 - image-color: white image-source: /core_styles/images/top_button.png image-clip: 0 0 26 26 image-border: 3 + image-color: #ffffffff + icon-color: #ffffffff $hover: - image-source: /core_styles/images/top_button.png + image-color: #ffffff99 image-clip: 26 0 26 26 - image-border: 3 $pressed: - image-source: /core_styles/images/top_button.png image-clip: 52 0 26 26 - image-border: 3 $disabled: - image-color: #ffffff66 + image-color: #ffffff44 + icon-color: #ffffff44 + +GameTopButton < UIButton + size: 26 26 + image-source: /core_styles/images/top_button2.png + image-clip: 26 0 26 26 + image-color: #ffffff22 + icon-color: #ffffffff + image-border: 3 + + $hover: + image-clip: 0 0 26 26 + image-color: #ffffffff + icon-color: #ffffffff + + $first: + anchors.top: parent.top + anchors.left: parent.left + margin-top: 4 + margin-left: 6 + + $!first: + anchors.top: prev.top + anchors.left: prev.right + margin-left: 6 TopLeftButton < TopButton $first: @@ -54,6 +77,13 @@ TopPanel anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: parent.left + width: 150 + + Panel + id: gameButtonsPanel + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: prev.right anchors.right: next.left Panel diff --git a/modules/core_lib/settings.lua b/modules/core_lib/settings.lua index 3a951821..cd2a51b2 100644 --- a/modules/core_lib/settings.lua +++ b/modules/core_lib/settings.lua @@ -10,6 +10,8 @@ local function convertSettingValue(value) return sizetostring(value) elseif value.r then return colortostring(value) + else + return value end elseif value == nil then return '' @@ -30,6 +32,10 @@ function Settings.set(key, value) g_configs.set(key, convertSettingValue(value)) end +function Settings.setList(key, list) + g_configs.setList(key, list) +end + function Settings.setDefault(key, value) if Settings.exists(key) then return false end Settings.set(key, value) @@ -43,6 +49,10 @@ function Settings.get(key, default) return g_configs.get(key) end +function Settings.getList(key) + return g_configs.getList(key) +end + function Settings.getString(key, default) return Settings.get(key, default) end diff --git a/modules/core_styles/icons/skills.png b/modules/core_styles/icons/skills.png index 0c46c9b9..52deb107 100644 Binary files a/modules/core_styles/icons/skills.png and b/modules/core_styles/icons/skills.png differ diff --git a/modules/core_styles/icons/viplist.png b/modules/core_styles/icons/viplist.png new file mode 100644 index 00000000..2bedcaff Binary files /dev/null and b/modules/core_styles/icons/viplist.png differ diff --git a/modules/core_styles/images/mini_window.png b/modules/core_styles/images/mini_window.png index d91d3c88..d3123826 100644 Binary files a/modules/core_styles/images/mini_window.png and b/modules/core_styles/images/mini_window.png differ diff --git a/modules/core_styles/images/top_button.png b/modules/core_styles/images/top_button.png index 6a8ef9d0..d0480416 100644 Binary files a/modules/core_styles/images/top_button.png and b/modules/core_styles/images/top_button.png differ diff --git a/modules/core_styles/images/top_button2.png b/modules/core_styles/images/top_button2.png new file mode 100644 index 00000000..7820d506 Binary files /dev/null and b/modules/core_styles/images/top_button2.png differ diff --git a/modules/core_widgets/uicombobox.lua b/modules/core_widgets/uicombobox.lua index a6f13e7e..ef08dbfa 100644 --- a/modules/core_widgets/uicombobox.lua +++ b/modules/core_widgets/uicombobox.lua @@ -2,16 +2,16 @@ UIComboBox = extends(UIWidget) function UIComboBox.create() local combobox = UIComboBox.internalCreate() - combobox.options = {} - combobox.currentIndex = -1 + combobox.m_options = {} + combobox.m_currentIndex = -1 return combobox end function UIComboBox:setCurrentOption(text) - if not self.options then return end - for i,v in ipairs(self.options) do - if v.text == text and self.currentIndex ~= i then - self.currentIndex = i + if not self.m_options then return end + for i,v in ipairs(self.m_options) do + if v.text == text and self.m_currentIndex ~= i then + self.m_currentIndex = i self:setText(text) self:onOptionChange(text, data) return @@ -20,15 +20,15 @@ function UIComboBox:setCurrentOption(text) end function UIComboBox:addOption(text, data) - table.insert(self.options, { text = text, data = data }) - local index = #self.options + table.insert(self.m_options, { text = text, data = data }) + local index = #self.m_options if index == 1 then self:setCurrentOption(text) end return index end function UIComboBox:onMousePress(mousePos, mouseButton) local menu = createWidget(self:getStyleName() .. 'PopupMenu', self) - for i,v in ipairs(self.options) do + for i,v in ipairs(self.m_options) do menu:addOption(v.text, function() self:setCurrentOption(v.text) end) end menu:setWidth(self:getWidth()) diff --git a/modules/core_widgets/uimessagebox.lua b/modules/core_widgets/uimessagebox.lua new file mode 100644 index 00000000..2c81a7cb --- /dev/null +++ b/modules/core_widgets/uimessagebox.lua @@ -0,0 +1,23 @@ +UIMessageBox = extends(UIWindow) + +function UIMessageBox.create(title, message) + local messagebox = UIMessageBox.internalCreate() + + messagebox:setText(title) + local messageLabel = self:getChildById('messageLabel') + label:setText(message) + label:resizeToText() + + window:setWidth(math.max(label:getWidth() + self:getPaddingLeft() + self:getPaddingRight(), self:getWidth())) + window:setHeight(label:getHeight() + self:getPaddingTop() + self:getPaddingBottom()) + + return messagebox +end + +function UIMessageBox:setTitle(title) +end + +function UIMessageBox:setMessage(message) +end + +function \ No newline at end of file diff --git a/modules/core_widgets/uiprogressbar.lua b/modules/core_widgets/uiprogressbar.lua index 404f50e3..5d7cb3fa 100644 --- a/modules/core_widgets/uiprogressbar.lua +++ b/modules/core_widgets/uiprogressbar.lua @@ -4,19 +4,28 @@ function UIProgressBar.create() local progressbar = UIProgressBar.internalCreate() progressbar:setFocusable(false) progressbar:setPhantom(true) - progressbar.percent = 0 - progressbar:setBackgroundSize({width = 1, height = 1}) + progressbar.m_percent = 0 + progressbar:updateBackground() return progressbar end function UIProgressBar:setPercent(percent) - self:setBackgroundHeight(self:getHeight()) - local width = (percent * self:getWidth())/100 - if width == 0 then width = 1 end - self:setBackgroundWidth(width) - self.percent = percent + self.m_percent = percent + self:updateBackground() end + function UIProgressBar:getPercent() - return self.percent + return self.m_percent +end + +function UIProgressBar:updateBackground() + local width = math.max((self.m_percent * self:getWidth())/100, 1) + local height = self:getHeight() + self:setBackgroundSize({width=width, height=height}) +end + + +function UIProgressBar:onGeometryChange(oldRect, newRect) + self:updateBackground() end diff --git a/modules/game/game.otmod b/modules/game/game.otmod index ec8c8334..cc5d2814 100644 --- a/modules/game/game.otmod +++ b/modules/game/game.otmod @@ -7,9 +7,9 @@ Module dependencies: - game_healthbar - game_inventory - - game_skills + //- game_skills - game_textmessage - //- game_viplist + - game_viplist - game_console - game_outfit - game_containers diff --git a/modules/game_skills/skills.lua b/modules/game_skills/skills.lua index 93f748d7..52f00567 100644 --- a/modules/game_skills/skills.lua +++ b/modules/game_skills/skills.lua @@ -43,8 +43,7 @@ end -- public functions function Skills.create() skillWindow = displayUI('skills.otui', { parent = Game.gameRightPanel }) - --skillsButton = TopMenu.addButton('skillsButton', 'Skills (Ctrl+S)', '/core_styles/icons/skills.png', Skills.toggle) - --skillsButton:setWidth(32) + skillsButton = TopMenu.addGameButton('skillsButton', 'Skills (Ctrl+S)', '/core_styles/icons/skills.png', Skills.toggle) end function Skills.destroy() diff --git a/modules/game_viplist/viplist.lua b/modules/game_viplist/viplist.lua index 4747d1cb..6ec5d994 100644 --- a/modules/game_viplist/viplist.lua +++ b/modules/game_viplist/viplist.lua @@ -7,6 +7,8 @@ local addVipWindow = nil -- public functions function VipList.create() vipWindow = displayUI('viplist.otui', { parent = Game.gameRightPanel }) + vipWindow:hide() + TopMenu.addGameButton('vipListButton', 'VIP list', '/core_styles/icons/viplist.png', VipList.toggle) end function VipList.destroy() @@ -43,21 +45,21 @@ function VipList.onAddVip(id, name, online) end label.vipOnline = online - + local nameLower = name:lower() local childrenCount = vipList:getChildCount() - + for i=1,childrenCount do local child = vipList:getChildByIndex(i) if online and not child.vipOnline then vipList:insertChild(i, label) return end - + if (not online and not child.vipOnline) or (online and child.vipOnline) then local childText = child:getText():lower() local length = math.min(childText:len(), nameLower:len()) - + for j=1,length do if nameLower:byte(j) < childText:byte(j) then vipList:insertChild(i, label) @@ -68,7 +70,7 @@ function VipList.onAddVip(id, name, online) end end end - + vipList:insertChild(childrenCount+1, label) end @@ -77,7 +79,7 @@ function VipList.onVipStateChange(id, online) local label = vipList:getChildById('vip' .. id) local text = label:getText() vipList:removeChild(label) - + VipList.onAddVip(id, text, online) end @@ -89,7 +91,7 @@ function VipList.onVipListMousePress(widget, mousePos, mouseButton) local menu = createWidget('PopupMenu') menu:addOption('Add new VIP', function() VipList.createAddWindow() end) menu:display(mousePos) - + return true end @@ -104,7 +106,7 @@ function VipList.onVipListLabelMousePress(widget, mousePos, mouseButton) menu:addSeparator() menu:addOption('Copy Name', function() g_window.setClipboardText(widget:getText()) end) menu:display(mousePos) - + return true end diff --git a/src/framework/core/configmanager.cpp b/src/framework/core/configmanager.cpp index 410c1851..a88a6365 100644 --- a/src/framework/core/configmanager.cpp +++ b/src/framework/core/configmanager.cpp @@ -27,6 +27,11 @@ ConfigManager g_configs; +ConfigManager::ConfigManager() +{ + m_confsDoc = OTMLDocument::create(); +} + bool ConfigManager::load(const std::string& file) { m_fileName = file; @@ -35,9 +40,9 @@ bool ConfigManager::load(const std::string& file) return false; try { - OTMLDocumentPtr doc = OTMLDocument::parse(file); - for(const OTMLNodePtr& child : doc->children()) - m_confsMap[child->tag()] = child->value(); + OTMLDocumentPtr confsDoc = OTMLDocument::parse(file); + if(confsDoc) + m_confsDoc = confsDoc; return true; } catch(Exception& e) { logError("could not load configurations: ", e.what()); @@ -47,13 +52,65 @@ bool ConfigManager::load(const std::string& file) bool ConfigManager::save() { - OTMLDocumentPtr doc = OTMLDocument::create(); - for(auto it : m_confsMap) { - if(it.second == "") - continue; - OTMLNodePtr node = OTMLNode::create(it.first, it.second); - doc->addChild(node); + if(m_fileName.length() == 0) + return false; + return m_confsDoc->save(m_fileName); +} + +void ConfigManager::set(const std::string& key, const std::string& value) +{ + if(key == "") { + remove(key); + return; + } + + OTMLNodePtr child = OTMLNode::create(key, value); + m_confsDoc->addChild(child); +} + +void ConfigManager::setList(const std::string& key, const std::vector& list) +{ + remove(key); + + if(list.size() == 0) + return; + + OTMLNodePtr child = OTMLNode::create(key, true); + for(const std::string& value : list) { + child->writeIn(value); + dump << "insert" << value; + } + m_confsDoc->addChild(child); +} + +bool ConfigManager::exists(const std::string& key) +{ + return m_confsDoc->hasChildAt(key); +} + +std::string ConfigManager::get(const std::string& key) +{ + OTMLNodePtr child = m_confsDoc->get(key); + if(child) + return child->value(); + else + return ""; +} + +std::vector ConfigManager::getList(const std::string& key) +{ + std::vector list; + OTMLNodePtr child = m_confsDoc->get(key); + if(child) { + for(const OTMLNodePtr& subchild : child->children()) + list.push_back(subchild->value()); } - return doc->save(m_fileName); + return list; } +void ConfigManager::remove(const std::string& key) +{ + OTMLNodePtr child = m_confsDoc->get(key); + if(child) + m_confsDoc->removeChild(child); +} diff --git a/src/framework/core/configmanager.h b/src/framework/core/configmanager.h index 8297b5f2..575836eb 100644 --- a/src/framework/core/configmanager.h +++ b/src/framework/core/configmanager.h @@ -24,21 +24,25 @@ #define CONFIGMANAGER_H #include "declarations.h" +#include class ConfigManager { public: + ConfigManager(); bool load(const std::string& file); bool save(); - bool exists(const std::string& key) { return m_confsMap.find(key) != m_confsMap.end(); } - void set(const std::string& key, const std::string& value) { m_confsMap[key] = value; } - std::string get(const std::string& key) { return m_confsMap[key]; } - void remove(const std::string& key) { m_confsMap[key] = ""; } + void set(const std::string& key, const std::string& value); + void setList(const std::string& key, const std::vector& list); + std::string get(const std::string& key); + std::vector getList(const std::string& key); + bool exists(const std::string& key); + void remove(const std::string& key); private: std::string m_fileName; - std::map m_confsMap; + OTMLDocumentPtr m_confsDoc; }; extern ConfigManager g_configs; diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index d1864bc6..a9daa038 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -366,7 +366,9 @@ void Application::registerLuaFunctions() // ConfigManager g_lua.registerStaticClass("g_configs"); g_lua.bindClassStaticFunction("g_configs", "set", std::bind(&ConfigManager::set, &g_configs, _1, _2)); + g_lua.bindClassStaticFunction("g_configs", "setList", std::bind(&ConfigManager::setList, &g_configs, _1, _2)); g_lua.bindClassStaticFunction("g_configs", "get", std::bind(&ConfigManager::get, &g_configs, _1)); + g_lua.bindClassStaticFunction("g_configs", "getList", std::bind(&ConfigManager::getList, &g_configs, _1)); g_lua.bindClassStaticFunction("g_configs", "exists", std::bind(&ConfigManager::exists, &g_configs, _1)); g_lua.bindClassStaticFunction("g_configs", "remove", std::bind(&ConfigManager::remove, &g_configs, _1)); diff --git a/src/framework/luascript/luavaluecasts.h b/src/framework/luascript/luavaluecasts.h index da0cb0c8..a45d2137 100644 --- a/src/framework/luascript/luavaluecasts.h +++ b/src/framework/luascript/luavaluecasts.h @@ -125,6 +125,9 @@ luavalue_cast(int index, std::function& func); template void push_luavalue(const std::vector& vec); +template +bool luavalue_cast(int index, std::vector& vec); + // deque template void push_luavalue(const std::deque& vec); @@ -249,6 +252,22 @@ void push_luavalue(const std::vector& vec) { } } +template +bool luavalue_cast(int index, std::vector& vec) +{ + if(g_lua.isTable(index)) { + g_lua.pushNil(); + while(g_lua.next(index < 0 ? index-1 : index)) { + T value; + if(luavalue_cast(-1, value)) + vec.push_back(value); + g_lua.pop(); + } + return true; + } + return false; +} + template void push_luavalue(const std::deque& vec) { g_lua.newTable();