diff --git a/LICENSE b/LICENSE index 8f7645d..b2c5812 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ 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. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index c8a62f1..3acfe88 100644 --- a/README.md +++ b/README.md @@ -3,26 +3,39 @@ LoGiVi is a git-repository visualisation tool inspired by [Gource](https://code.google.com/p/gource/) and __currently in development__. It was written from scratch using [Lua](http://www.lua.org/) and the [LÖVE](https://love2d.org/) framework. # Instructions -LoGiVi can't read from a .git repository directly ([yet](https://github.com/rm-code/logivi/issues/3)). Instead you will have to create a git-log which needs to have a specific format. Please use this command to create the file: +When you run LoGiVi for the first time it will set up all necessary folders, an example git log and a config file in the save directory on your harddrive. A dialog will pop up which allows you to view the save directory. -```bash -git log --reverse --numstat --pretty=format:"author: %an|%ae%ndate: %ct%n" --name-status --no-merges > log.txt +## Generating git logs automatically +LoGiVi can generate git logs automatically when you specify a path to a git repository on your harddrive. Open the _settings.cfg_ file in the LoGiVi save directory and look for the _[repositories]_ section. Add the absolute path to the folder containing the git repository like this: + +``` +[repositories] +logivi = /Users/Robert/Coding/Lua/LÖVE/LoGiVi/ ``` +The name on the left side of the equals sign will be used as the project name to identify this repository so make sure you use unique names here. -This will create a log file in the same directory as the .git repository (If you want to write the log to a different location add its path after the '>' in the command above). +LoGiVi can also handle Windows paths: -When you run LoGiVi for the first time it will automatically open the folder in which you need to place the log. Depending on your operating system this can be one of the following locations: +``` +[repositories] +logivi = C:\Users\rmcode\Documents\Coding Projects\LoGiVi\ +``` +After you have added the paths of your project to the config file, the log and info files will be created the next time you run LoGiVi (this may take a few seconds depending on how large the repositories are). -- Windows XP: C:\Documents and Settings\user\Application Data\LOVE\rmcode_logivi -- Windows Vista and 7: C:\Users\user\AppData\Roaming\LOVE\rmcode_logivi -- Linux: $XDG_DATA_HOME/love/ or ~/.local/share/love/rmcode_logivi -- Mac: /Users/user/Library/Application Support/LOVE/rmcode_logivi +## Generating git logs manually +If you don't want the logs to be generated automatically, or if you don't have git in your PATH, you can also generate the git logs manually. -For more information about the filesystem check the [LÖVE wiki](https://love2d.org/wiki/love.filesystem). +Open your terminal and type in the following command (replace the path with your own path leading to a git repository): -As soon as the file is in the correct folder you can start LoGiVi and watch as it creates a visual representation of your git repository. +```bash +git -C "Path/To/Your/Repository" log --reverse --numstat --pretty=format:"info: %an|%ae|%ct" --name-status --no-merges > log.txt +``` +This will create the file _log.txt_ in the folder you are currently in. Take this newly created file and drop it into a folder in the _logs_ subfolder in the LoGiVi save directory: -Check the [wiki](https://github.com/rm-code/logivi/wiki) for instructions and further information. +``` +/Users/Robert/Library/Application Support/LOVE/rmcode_LoGiVi/logs/yourProject/log.txt +``` +LoGiVi will use the folder's name to identify the log so make it informative. # License @@ -46,4 +59,4 @@ 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. \ No newline at end of file +SOFTWARE. diff --git a/conf.lua b/conf.lua index a1089cb..f94efeb 100644 --- a/conf.lua +++ b/conf.lua @@ -22,19 +22,15 @@ local PROJECT_TITLE = "LoGiVi"; -local PROJECT_VERSION = "0204"; +local PROJECT_VERSION = "0312"; local PROJECT_IDENTITY = "rmcode_LoGiVi"; local LOVE_VERSION = "0.9.2"; --- ------------------------------------------------ --- Global Functions --- ------------------------------------------------ - --- -- Initialise löve's config file. --- @param _conf +-- @param t -- function love.conf(t) t.identity = PROJECT_IDENTITY; diff --git a/lib/screenmanager/ScreenManager.lua b/lib/screenmanager/ScreenManager.lua index 244f367..174faca 100644 --- a/lib/screenmanager/ScreenManager.lua +++ b/lib/screenmanager/ScreenManager.lua @@ -21,7 +21,7 @@ --===============================================================================-- local ScreenManager = { - _VERSION = '1.3.1', + _VERSION = '1.4.1', _DESCRIPTION = 'Screen/State Management for the LÖVE framework', _URL = 'https://github.com/rm-code/screenmanager/', }; @@ -69,9 +69,9 @@ end -- -- @param nscreen -- -function ScreenManager.switch(screen) +function ScreenManager.switch(screen, ...) clear(); - ScreenManager.push(screen); + ScreenManager.push(screen, ...); end --- @@ -81,7 +81,7 @@ end -- -- @param screen - The name of the screen to push on the stack. -- -function ScreenManager.push(screen) +function ScreenManager.push(screen, ...) -- Deactivate the previous screen if there is one. if ScreenManager.peek() then ScreenManager.peek():setActive(false); @@ -100,7 +100,7 @@ function ScreenManager.push(screen) end -- Create the new screen and initialise it. - stack[#stack]:init(); + stack[#stack]:init(...); end --- diff --git a/main.lua b/main.lua index ccc8e1a..6e3029e 100644 --- a/main.lua +++ b/main.lua @@ -51,6 +51,9 @@ local function checkSupport() print("\n---- RENDERER ---- "); local name, version, vendor, device = love.graphics.getRendererInfo() print(string.format("Name: %s \nVersion: %s \nVendor: %s \nDevice: %s", name, version, vendor, device)); + + print("\n---- SYSTEM ---- "); + print(love.system.getOS()); end local function drawStats() @@ -88,10 +91,11 @@ function love.load() print("===================") local screens = { + selection = require('src.screens.SelectionScreen'); main = require('src.screens.MainScreen'); }; - ScreenManager.init(screens, 'main'); + ScreenManager.init(screens, 'selection'); end function love.draw() @@ -110,7 +114,17 @@ function love.quit(q) ScreenManager.quit(q); end +function love.resize(x, y) + ScreenManager.resize(x, y); +end + function love.keypressed(key) + if key == ' ' then + key = 'space'; + elseif tonumber(key) then + key = tonumber(key); + end + if key == 'f1' then showDebug = not showDebug; end @@ -128,4 +142,4 @@ end function love.mousemoved(x, y, dx, dy) ScreenManager.mousemoved(x, y, dx, dy); -end \ No newline at end of file +end diff --git a/res/fonts/SourceCodePro-Bold.otf b/res/fonts/SourceCodePro-Bold.otf new file mode 100755 index 0000000..96b4877 Binary files /dev/null and b/res/fonts/SourceCodePro-Bold.otf differ diff --git a/res/img/avatar.png b/res/img/avatar.png new file mode 100644 index 0000000..9fbac7b Binary files /dev/null and b/res/img/avatar.png differ diff --git a/res/img/file.png b/res/img/file.png index ec194fc..33b4bee 100644 Binary files a/res/img/file.png and b/res/img/file.png differ diff --git a/res/img/user.png b/res/img/user.png deleted file mode 100644 index 027a9b3..0000000 Binary files a/res/img/user.png and /dev/null differ diff --git a/res/templates/example_log.txt b/res/templates/example_log.txt new file mode 100644 index 0000000..1975a1b --- /dev/null +++ b/res/templates/example_log.txt @@ -0,0 +1,981 @@ +info: Robert Machmer|robert.machmer@gmail.com|1412164839 +A Readme.md +A conf.lua +A lib/Screen.lua +A lib/ScreenManager.lua +A main.lua +A src/FileHandler.lua +A src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412165331 +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412165654 +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412166102 +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412166546 +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412168721 +A res/fileNode.png +A src/FileObject.lua +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412261158 +M src/FileHandler.lua +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412263695 +M src/FileHandler.lua +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412264556 +A res/folderNode.png +M src/FileHandler.lua +A src/FileNode.lua +D src/FileObject.lua +A src/FolderNode.lua +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412266778 +M src/FolderNode.lua +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412266994 +M src/FileHandler.lua +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412338837 +M src/FileNode.lua +M src/FolderNode.lua +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412338894 +D src/FileNode.lua +D src/FolderNode.lua +M src/MainScreen.lua +A src/nodes/FileNode.lua +A src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412339571 +M src/nodes/FileNode.lua +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1412344563 +M src/MainScreen.lua +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1413460386 +M src/nodes/FileNode.lua +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1413543677 +M src/nodes/FileNode.lua +M src/nodes/FolderNode.lua +A src/nodes/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1413545829 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1413641316 +M main.lua + +info: Robert Machmer|robert.machmer@gmail.com|1413647269 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1413746796 +M src/MainScreen.lua +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420035671 +M src/FileHandler.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420035782 +M src/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420036758 +M Readme.md + +info: Robert Machmer|robert.machmer@gmail.com|1420119343 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420127901 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420128096 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420128221 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420142059 +M lib/Screen.lua +M lib/ScreenManager.lua +M main.lua +D src/MainScreen.lua +A src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1420148522 +A lib/Camera.lua +M src/nodes/FolderNode.lua +M src/nodes/Node.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421154008 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421154091 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421170328 +A src/Authors.lua +M src/FileHandler.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421170600 +M conf.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421198671 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421202411 +A src/FileManager.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421205955 +D src/FileHandler.lua +A src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421206481 +M src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421227556 +M res/fileNode.png +M src/FileManager.lua +M src/nodes/FileNode.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421231021 +M Readme.md + +info: Robert Machmer|robert.machmer@gmail.com|1421396416 +M src/Authors.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421401857 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421402864 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421408663 +M Readme.md + +info: Robert Machmer|robert.machmer@gmail.com|1421509527 +A src/AuthorManager.lua +D src/Authors.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421519652 +M src/nodes/FileNode.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421523219 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421528929 +A src/Author.lua +M src/AuthorManager.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421539184 +M src/Author.lua +M src/AuthorManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421539872 +M src/Author.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421540698 +M src/AuthorManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421547891 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421548797 +M conf.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421549312 +M conf.lua +M main.lua +M src/Author.lua +M src/AuthorManager.lua +M src/FileManager.lua +M src/LogReader.lua +M src/nodes/FileNode.lua +M src/nodes/FolderNode.lua +M src/nodes/Node.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421550122 +M main.lua +M src/Author.lua +M src/AuthorManager.lua +M src/FileManager.lua +M src/LogReader.lua +M src/nodes/FileNode.lua +M src/nodes/FolderNode.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421579809 +M src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421581498 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421587878 +M conf.lua +M main.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421589831 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421590288 +D res/folderNode.png +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421591304 +M src/nodes/FolderNode.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421596235 +M src/AuthorManager.lua +A src/ConfigReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421596666 +M src/ConfigReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421597404 +M src/AuthorManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421600497 +D lib/Screen.lua +D lib/ScreenManager.lua +A lib/screenmanager/Readme.md +A lib/screenmanager/Screen.lua +A lib/screenmanager/ScreenManager.lua +M main.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421603864 +M main.lua +M src/ConfigReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421719866 +M conf.lua +M src/ConfigReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421833270 +M lib/Camera.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421838188 +A res/file.png +D res/fileNode.png +M src/LogReader.lua +A src/graph/File.lua +A src/graph/Graph.lua +A src/graph/Node.lua +D src/nodes/FileNode.lua +D src/nodes/FolderNode.lua +D src/nodes/Node.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421972644 +A res/user.png +M src/AuthorManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1421975143 +M src/AuthorManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1422185971 +M src/graph/Graph.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1422186427 +M src/Author.lua +M src/AuthorManager.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1422786015 +M src/AuthorManager.lua +M src/ConfigReader.lua +M src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1422837441 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1422960161 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1422962666 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1422971825 +M lib/screenmanager/ScreenManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423320202 +M src/graph/File.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423403469 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423445606 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423445842 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423446229 +M main.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423900936 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423901214 +M src/graph/File.lua +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1423992268 +M src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427529076 +M res/file.png +M src/graph/Node.lua + +info: rmcode|robert.machmer@gmail.com|1427660215 +M Readme.md + +info: Robert Machmer|robert.machmer@gmail.com|1427660238 +M main.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427662791 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427663671 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427663911 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427664089 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427664992 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427665463 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427665685 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427665887 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427667043 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427667629 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427667755 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427667862 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427669309 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427670123 +D lib/Camera.lua +A lib/camera/Camera.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427674743 +M res/user.png + +info: Robert Machmer|robert.machmer@gmail.com|1427674989 +M conf.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427706266 +M main.lua +M src/AuthorManager.lua +M src/graph/File.lua +M src/graph/Graph.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427706761 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427711700 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427728116 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427728127 +M src/graph/File.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427729076 +M src/ConfigReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427730189 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427753588 +M conf.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427753963 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427755389 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427808901 +M lib/screenmanager/Screen.lua +M lib/screenmanager/ScreenManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427816735 +M src/Author.lua +M src/AuthorManager.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427818995 +M main.lua +M src/FileManager.lua +M src/screens/MainScreen.lua +A src/ui/Panel.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427819306 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427837032 +M src/Author.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427840420 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427847257 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427848414 +D src/ConfigReader.lua +A src/conf/ConfigReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427849379 +M src/conf/ConfigReader.lua +A src/conf/Template.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427850457 +M src/conf/ConfigReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427850600 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427851668 +M src/AuthorManager.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427851674 +M main.lua +M src/AuthorManager.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427851917 +M src/AuthorManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427880295 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427882656 +M src/conf/Template.lua +M src/graph/Graph.lua +M src/graph/Node.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427883181 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427883392 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427884293 +M src/Author.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427884826 +M src/Author.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427885169 +A LICENSE + +info: rmcode|robert.machmer@gmail.com|1427885243 +A README.md +D Readme.md + +info: Robert Machmer|robert.machmer@gmail.com|1427886399 +M README.md + +info: Robert Machmer|robert.machmer@gmail.com|1427886841 +D res/file.png +A res/img/file.png +A res/img/user.png +D res/user.png +M src/AuthorManager.lua +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427892008 +A res/fonts/SIL Open Font License.txt +A res/fonts/SourceCodePro-Medium.otf +M src/graph/Graph.lua +M src/graph/Node.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427892194 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427892445 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427892644 +M conf.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427894190 +M conf.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427894264 +M conf.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427897659 +M src/conf/Template.lua +M src/graph/Graph.lua +M src/graph/Node.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427898068 +M src/conf/Template.lua +M src/graph/Graph.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427936097 +M src/LogReader.lua +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427936619 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427936897 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427976666 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427976679 +M src/Author.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427993275 +M src/AuthorManager.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427994856 +M src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427995775 +M src/LogReader.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1427995792 +M src/LogReader.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428005944 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428015869 +M src/LogReader.lua +M src/conf/Template.lua +M src/graph/Graph.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428015967 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428018314 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428018880 +M src/LogReader.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428053610 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428073565 +M src/conf/Template.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428104660 +M src/Author.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428105158 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428105654 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428106654 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428163411 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428188837 +M src/LogReader.lua +A src/Timeline.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428191406 +D src/Timeline.lua +M src/screens/MainScreen.lua +A src/ui/Timeline.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428196160 +M src/graph/Graph.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428197088 +M src/ui/Panel.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428198089 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428198761 +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428224954 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428277319 +M src/LogReader.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428279440 +M src/LogReader.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428280587 +M src/Author.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428309651 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428339053 +M src/LogReader.lua +M src/screens/MainScreen.lua +M src/ui/Timeline.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428350427 +M README.md + +info: Robert Machmer|robert.machmer@gmail.com|1428357571 +M src/LogReader.lua +M src/ui/Timeline.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428363849 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428370108 +M src/FileManager.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428399939 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428400533 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428401257 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428521580 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428535218 +M src/AuthorManager.lua +M src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428535519 +M src/AuthorManager.lua +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428571917 +M src/LogReader.lua +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428578992 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428579312 +M src/AuthorManager.lua +M src/graph/Graph.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428579477 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428581353 +M src/AuthorManager.lua +M src/LogReader.lua +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428581987 +M src/LogReader.lua +M src/graph/Graph.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428583714 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428583741 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428583812 +M src/graph/Graph.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428591387 +M src/graph/File.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428592678 +M src/graph/File.lua +M src/graph/Node.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428662955 +M src/ui/Timeline.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428667958 +M src/LogReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428670174 +M conf.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428706037 +M src/ui/Panel.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428713834 +M src/screens/MainScreen.lua + +info: EntranceJew|EntranceJew@gmail.com|1428715691 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428715835 +M src/conf/Template.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428716450 +A src/InputHandler.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428717805 +M src/screens/MainScreen.lua +A src/ui/CamWrapper.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428747481 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428762289 +A src/LogLoader.lua +M src/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428762480 +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428762751 +D src/LogLoader.lua +D src/LogReader.lua +A src/logfactory/LogLoader.lua +A src/logfactory/LogReader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428833130 +M src/logfactory/LogLoader.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428833413 +M main.lua +M src/logfactory/LogLoader.lua +M src/screens/MainScreen.lua +A src/screens/SelectionScreen.lua +A src/ui/Button.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428833628 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428834468 +M src/conf/ConfigReader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428835134 +M src/screens/MainScreen.lua +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428837440 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428840719 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428846771 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428857142 +M src/ui/Button.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428857574 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428863026 +M src/screens/SelectionScreen.lua +M src/ui/Button.lua +A src/ui/ButtonList.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428865168 +M src/ui/Button.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428865771 +A res/fonts/SourceCodePro-Bold.otf +M src/screens/SelectionScreen.lua +M src/ui/ButtonList.lua +A src/ui/InfoPanel.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428867077 +M src/screens/SelectionScreen.lua +M src/ui/Button.lua +M src/ui/ButtonList.lua +M src/ui/InfoPanel.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428867121 +M src/ui/Button.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428867160 +M src/ui/Button.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428915681 +M src/conf/Template.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428916708 +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428918486 +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428919369 +A res/log/example_log.txt +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428919664 +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428919695 +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428919952 +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428921032 +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428931073 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428931540 +M main.lua +M src/screens/SelectionScreen.lua +M src/ui/Button.lua +M src/ui/InfoPanel.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428937253 +M src/screens/SelectionScreen.lua +M src/ui/ButtonList.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428937278 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428956825 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428957125 +M src/screens/SelectionScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428957337 +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428957688 +M README.md + +info: Robert Machmer|robert.machmer@gmail.com|1428959105 +M src/FileManager.lua +M src/conf/Template.lua +M src/screens/MainScreen.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428996976 +M src/logfactory/LogLoader.lua + +info: Robert Machmer|robert.machmer@gmail.com|1428997224 +M README.md diff --git a/res/templates/settings.cfg b/res/templates/settings.cfg new file mode 100644 index 0000000..2d59da8 --- /dev/null +++ b/res/templates/settings.cfg @@ -0,0 +1,58 @@ +; ------------------------------- +; LoGiVi - Configuration File +; ------------------------------- + +[aliases] +; email = author + +[avatars] +; author = urlToAvatar + +[colors] +; .lua = 255, 0, 0 + +[repositories] +; project = path/to/repository + +[options] +mode = default +autoplay = true +showFileList = true +showAuthors = true +showLabels = true +showTimeline = true +commitDelay = 0.2 +edgeWidth = 5 +backgroundColor = 0, 0, 0 +removeTmpFiles = false +screenWidth = 0 +screenHeight = 0 +fullscreen = true +fullscreenType = desktop +vsync = true +msaa = 0 +display = 1 + +[keyBindings] +camera_n = w +camera_w = a +camera_s = s +camera_e = d +camera_rotateL = q +camera_rotateR = e +camera_zoomIn = +, up +camera_zoomOut = -, down + +toggleAuthors = 1 +toggleFileList = 2 +toggleLabels = 3 +toggleTimeline = 4 + +toggleSimulation = space +toggleRewind = backspace +loadNextCommit = right +loadPrevCommit = left + +toggleFullscreen = f + +exit = escape diff --git a/src/Author.lua b/src/Author.lua index 18e8438..c0ec859 100644 --- a/src/Author.lua +++ b/src/Author.lua @@ -27,15 +27,22 @@ local Author = {}; -- ------------------------------------------------ local AVATAR_SIZE = 48; -local AUTHOR_INACTIVITY_TIMER = 10; -local LINK_INACTIVITY_TIMER = 3; +local AUTHOR_INACTIVITY_TIMER = 2; +local LINK_INACTIVITY_TIMER = 2; local FADE_FACTOR = 2; local DEFAULT_AVATAR_ALPHA = 255; -local DEFAULT_LINK_ALPHA = 50; -local DEFAULT_DAMPING_VALUE = 0.5; -local MIN_DISTANCE = 400; +local DEFAULT_LINK_ALPHA = 100; +local DAMPING_FACTOR = 0.90; +local FORCE_MAX = 2; +local FORCE_SPRING = -0.5; local BEAM_WIDTH = 3; +local LINK_COLOR = { + A = { 0, 255, 0 }, + D = { 255, 0, 0 }, + M = { 254, 140, 0 }, +}; + -- ------------------------------------------------ -- Constructor -- ------------------------------------------------ @@ -43,7 +50,7 @@ local BEAM_WIDTH = 3; function Author.new(name, avatar, cx, cy) local self = {}; - local posX, posY = cx + love.math.random(5, 40) * (love.math.random(0, 1) == 0 and -1 or 1), cy + love.math.random(5, 40) * (love.math.random(0, 1) == 0 and -1 or 1); + local posX, posY = cx + love.math.random(5, 200) * (love.math.random(0, 1) == 0 and -1 or 1), cy + love.math.random(5, 200) * (love.math.random(0, 1) == 0 and -1 or 1); local accX, accY = 0, 0; local velX, velY = 0, 0; @@ -55,64 +62,30 @@ function Author.new(name, avatar, cx, cy) -- Avatar's width and height. local aw, ah = avatar:getWidth(), avatar:getHeight(); - local dampingFactor = DEFAULT_DAMPING_VALUE; - -- ------------------------------------------------ -- Private Functions -- ------------------------------------------------ + local function clamp(min, val, max) + return math.max(min, math.min(val, max)); + end + local function reactivate() inactivity = 0; - dampingFactor = DEFAULT_DAMPING_VALUE; avatarAlpha = DEFAULT_AVATAR_ALPHA; linkAlpha = DEFAULT_LINK_ALPHA; end local function move(dt) - local dx, dy; - local distance - - for i = 1, #links do - local file = links[i]; - - -- Attract - dx, dy = posX - file:getX(), posY - file:getY(); - distance = math.sqrt(dx * dx + dy * dy); - - -- Normalise vector. - dx = dx / distance; - dy = dy / distance; - - -- Attraction. - if distance > MIN_DISTANCE then - accX = dx * -distance * 5; - accY = dy * -distance * 5; - end - - -- Repulsion. - accX = accX + (dx * distance); - accY = accY + (dy * distance); - end - - -- Repel from the graph's center. - dx, dy = posX - cx, posY - cy; - distance = math.sqrt(dx * dx + dy * dy); - dx = dx / distance; - dy = dy / distance; - accX = accX + (dx * distance); - accY = accY + (dy * distance); - - accX = math.max(-4, math.min(accX, 4)); - accY = math.max(-4, math.min(accY, 4)); - - velX = velX + accX * dt * 16; - velY = velY + accY * dt * 16; - + velX = (velX + accX * dt * 32) * DAMPING_FACTOR; + velY = (velY + accY * dt * 32) * DAMPING_FACTOR; posX = posX + velX; posY = posY + velY; + end - velX = velX * dampingFactor; - velY = velY * dampingFactor; + local function applyForce(fx, fy) + accX = clamp(-FORCE_MAX, accX + fx, FORCE_MAX); + accY = clamp(-FORCE_MAX, accY + fy, FORCE_MAX); end -- ------------------------------------------------ @@ -121,9 +94,9 @@ function Author.new(name, avatar, cx, cy) function self:draw(rotation) for i = 1, #links do - love.graphics.setColor(255, 255, 255, linkAlpha); + love.graphics.setColor(LINK_COLOR[links[i].mod][1], LINK_COLOR[links[i].mod][2], LINK_COLOR[links[i].mod][3], linkAlpha); love.graphics.setLineWidth(BEAM_WIDTH); - love.graphics.line(posX, posY, links[i]:getX(), links[i]:getY()); + love.graphics.line(posX, posY, links[i].file:getX(), links[i].file:getY()); love.graphics.setLineWidth(1); love.graphics.setColor(255, 255, 255, 255); end @@ -142,12 +115,22 @@ function Author.new(name, avatar, cx, cy) if inactivity > LINK_INACTIVITY_TIMER then linkAlpha = linkAlpha - linkAlpha * dt * FADE_FACTOR; end - dampingFactor = math.max(0.01, dampingFactor - dt); + if inactivity > 0.5 then + accX, accY = 0, 0; + end end - function self:addLink(file) + function self:addLink(file, modifier) reactivate(); - links[#links + 1] = file; + links[#links + 1] = { file = file, mod = modifier }; + + local dx, dy = posX - file:getX(), posY - file:getY(); + local distance = math.sqrt(dx * dx + dy * dy); + dx = dx / distance; + dy = dy / distance; + + local strength = FORCE_SPRING * distance; + applyForce(dx * strength, dy * strength); end function self:resetLinks() diff --git a/src/AuthorManager.lua b/src/AuthorManager.lua index a266b26..ee2eccc 100644 --- a/src/AuthorManager.lua +++ b/src/AuthorManager.lua @@ -34,6 +34,7 @@ local AuthorManager = {}; -- ------------------------------------------------ local PATH_AVATARS = 'tmp/avatars/'; +local PATH_DEFAULT_AVATAR = 'res/img/avatar.png'; -- ------------------------------------------------ -- Local Variables @@ -89,7 +90,7 @@ local function grabAvatars(urlList) end -- Load the default user avatar. - avatars['default'] = love.graphics.newImage('res/img/user.png'); + avatars['default'] = love.graphics.newImage(PATH_DEFAULT_AVATAR); return avatars; end @@ -138,8 +139,8 @@ end -- Adds a link from the current author to a file. -- @param file -- -function AuthorManager.addFileLink(file) - activeAuthor:addLink(file) +function AuthorManager.addFileLink(file, modifier) + activeAuthor:addLink(file, modifier) end --- diff --git a/src/FileManager.lua b/src/FileManager.lua index b0b791c..52fa549 100644 --- a/src/FileManager.lua +++ b/src/FileManager.lua @@ -36,6 +36,7 @@ local SCND_OFFSET = 50; local extensions = {}; local sortedList = {}; local totalFiles = 0; +local colors; -- ------------------------------------------------ -- Local Functions @@ -97,14 +98,14 @@ function FileManager.add(fileName) extensions[ext] = {}; extensions[ext].extension = ext; extensions[ext].amount = 0; - extensions[ext].color = { love.math.random(0, 255), love.math.random(0, 255), love.math.random(0, 255) }; + extensions[ext].color = colors[ext] or { love.math.random(0, 255), love.math.random(0, 255), love.math.random(0, 255) }; end extensions[ext].amount = extensions[ext].amount + 1; totalFiles = totalFiles + 1; createSortedList(extensions); - return extensions[ext].color; + return extensions[ext].color, ext; end --- @@ -128,6 +129,13 @@ function FileManager.remove(fileName) createSortedList(extensions); end +function FileManager.reset() + extensions = {}; + sortedList = {}; + totalFiles = 0; + colors = nil; +end + -- ------------------------------------------------ -- Getters -- ------------------------------------------------ @@ -139,6 +147,17 @@ function FileManager.getColor(ext) return extensions[ext].color; end +-- ------------------------------------------------ +-- Setters +-- ------------------------------------------------ + +--- +-- @param ncol +-- +function FileManager.setColorTable(ncol) + colors = ncol; +end + -- ------------------------------------------------ -- Return Module -- ------------------------------------------------ diff --git a/src/InputHandler.lua b/src/InputHandler.lua new file mode 100644 index 0000000..d6799cb --- /dev/null +++ b/src/InputHandler.lua @@ -0,0 +1,60 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local InputHandler = {}; + +--- +-- Determines if a key constant or if any in a table of key constants are down. +-- @param constant - The key constant or table of constants. +-- +function InputHandler.isDown(constant) + if type(constant) == 'table' then + for _, keyToCheck in ipairs(constant) do + if love.keyboard.isDown(keyToCheck) then + return true; + end + end + return false; + else + return love.keyboard.isDown(constant); + end +end + +--- +-- Determines if a key constant or if any in a table of key constants was pressed. +-- @param key - The key to check for. +-- @param constant - The key constant or table of constants. +-- +function InputHandler.isPressed(key, constant) + if type(constant) == 'table' then + for _, keyToCheck in ipairs(constant) do + if key == keyToCheck then + return true; + end + end + return false; + else + return key == constant; + end +end + +return InputHandler; diff --git a/src/conf/ConfigReader.lua b/src/conf/ConfigReader.lua index 24f1180..8990fbb 100644 --- a/src/conf/ConfigReader.lua +++ b/src/conf/ConfigReader.lua @@ -26,27 +26,103 @@ local ConfigReader = {}; -- Constants -- ------------------------------------------------ -local FILE_NAME = 'config.lua'; -local FILE_TEMPLATE = require('src.conf.Template'); +local FILE_NAME = 'settings.cfg'; +local TEMPLATE_PATH = 'res/templates/settings.cfg'; -- ------------------------------------------------ -- Local Variables -- ------------------------------------------------ +local default; local config; -- ------------------------------------------------ -- Local Functions -- ------------------------------------------------ -local function loadFile(name, default) - if not love.filesystem.isFile(name) then - local file = love.filesystem.newFile(name); - file:open('w'); - file:write(default); - file:close(); +local function hasConfigFile() + return love.filesystem.isFile(FILE_NAME); +end + +local function createConfigFile(name, default) + for line in love.filesystem.lines(default) do + love.filesystem.append(name, line .. '\r\n'); + end +end + +local function toType(value) + value = value:match('^%s*(.-)%s*$'); + if value == 'true' then + return true; + elseif value == 'false' then + return false; + elseif tonumber(value) then + return tonumber(value); + else + return value; end - return love.filesystem.load(name)(); +end + +local function loadFile(file) + local config = {}; + local section; + for line in love.filesystem.lines(file) do + if line == '' or line:find(';') == 1 then + -- Ignore comments and empty lines. + elseif line:match('^%[(%w*)%]$') then + -- Create a new section. + local header = line:match('^%[(%w*)%]$'); + config[header] = {}; + section = config[header]; + else + -- Store values in the section. + local key, value = line:match('^([%w_]+)%s-=%s-(.+)'); + + -- Store multiple values in a table. + if value and value:find(',') then + section[key] = {}; + for val in value:gmatch('[^, ]+') do + section[key][#section[key] + 1] = toType(val); + end + elseif value then + section[key] = toType(value); + end + end + end + + -- Validate file paths. + for project, path in pairs(config.repositories) do + config.repositories[project] = path:gsub('\\+', '/'); + end + + return config; +end + +local function validateFile(default, loaded) + print('Validating configuration file ... '); + for skey, section in pairs(default) do + + -- If loaded config file doesn't contain section return default. + if loaded[skey] == nil then + love.window.showMessageBox('Invalid config file', 'Seems like the loaded configuration file is missing the "' .. + skey .. '" section. The default settings will be used instead.', 'warning', false); + return default; + end + + if type(section) == 'table' then + for vkey, _ in pairs(section) do + if loaded[skey][vkey] == nil then + love.window.showMessageBox('Invalid config file', + 'Seems like the loaded configuration file is missing the "' .. + vkey .. '" value in the "' .. skey .. '" section. The default settings will be used instead.', 'warning', false); + return default; + end + end + end + end + + print('Done!'); + return loaded; end -- ------------------------------------------------ @@ -54,7 +130,18 @@ end -- ------------------------------------------------ function ConfigReader.init() - config = loadFile(FILE_NAME, FILE_TEMPLATE); + default = loadFile(TEMPLATE_PATH); + + if not hasConfigFile() then + createConfigFile(FILE_NAME, TEMPLATE_PATH); + end + + -- If the config hasn't been loaded yet, load and validate it. + if not config then + config = loadFile(FILE_NAME); + config = validateFile(default, config); + end + return config; end @@ -93,4 +180,4 @@ end -- Return Module -- ------------------------------------------------ -return ConfigReader; \ No newline at end of file +return ConfigReader; diff --git a/src/conf/Template.lua b/src/conf/Template.lua deleted file mode 100644 index 395f886..0000000 --- a/src/conf/Template.lua +++ /dev/null @@ -1,60 +0,0 @@ -return [[ --- ------------------------------- -- --- LoGiVi - Configuration File. -- --- ------------------------------- -- - -return { - -- Associates an email address with a author. This name will - -- then be used instead of the one found in the git log. - aliases = { - -- ['email'] = 'Author', - }, - -- Assigns an avatar to an author. - avatars = { - -- ['author'] = 'urlToAvatar', - }, - options = { - mode = 'default', -- 'default' or 'rewind' - autoplay = true, - showFileList = true, - showAuthors = true, - showLabels = true, - showTimeline = true, - commitDelay = 0.2, - edgeWidth = 5, - backgroundColor = { 0, 0, 0 }, - removeTmpFiles = false, - screenWidth = 0, - screenHeight = 0, - fullscreen = true, - fullscreenType = 'desktop', - vsync = true, - msaa = 0, - display = 1, - }, - - -- See https://love2d.org/wiki/KeyConstant for a list of possible keycodes. - keyBindings = { - camera_n = 'w', -- Move camera up - camera_w = 'a', -- Move camera left - camera_s = 's', -- Move camera down - camera_e = 'd', -- Move camera right - camera_rotateL = 'q', -- Rotate camera left - camera_rotateR = 'e', -- Rotate camera right - camera_zoomIn = '+', -- Zoom in - camera_zoomOut = '-', -- Zoom out - - toggleAuthors = '1', -- Hide / Show authors - toggleFileList = '2', -- Hide / Show file panel - toggleLabels = '3', -- Hide / Show folder labels - toggleTimeline = '4', -- Hide / Show timeline - - toggleSimulation = ' ', -- Stop / Play the simulation - toggleRewind = 'backspace', -- Make simulation run backwards - loadNextCommit = 'right', -- Manually load the next commit - loadPrevCommit = 'left', -- Manually load the previous commit - - toggleFullscreen = 'f', -- Toggle fullscreen - }, -}; -]] \ No newline at end of file diff --git a/src/graph/File.lua b/src/graph/File.lua index f521403..ca63554 100644 --- a/src/graph/File.lua +++ b/src/graph/File.lua @@ -48,7 +48,7 @@ function File.new(name, x, y) local posX, posY = x, y; local offX, offY = 0, 0; - local fileColor = FileManager.add(name); + local fileColor, extension = FileManager.add(name); local currentColor = {}; local modified = false; local timer = MOD_TIMER; @@ -118,6 +118,10 @@ function File.new(name, x, y) return currentColor; end + function self:getExtension() + return extension; + end + -- ------------------------------------------------ -- Setters -- ------------------------------------------------ diff --git a/src/graph/Graph.lua b/src/graph/Graph.lua index fa6a9fb..b1399b5 100644 --- a/src/graph/Graph.lua +++ b/src/graph/Graph.lua @@ -185,8 +185,8 @@ function Graph.new(ewidth, showLabels) end -- We only notify observers if the graph isn't modifed in fast forward / rewind mode. - if mode == 'normal' then - notify(EVENT_UPDATE_FILE, modifiedFile); + if mode == 'normal' and modifiedFile then + notify(EVENT_UPDATE_FILE, modifiedFile, modifier); end end diff --git a/src/graph/Node.lua b/src/graph/Node.lua index 2455331..22a27e8 100644 --- a/src/graph/Node.lua +++ b/src/graph/Node.lua @@ -28,8 +28,10 @@ local Node = {}; local FORCE_MAX = 4; -local SPRITE_SIZE = 0.45; -local SPRITE_OFFSET = 15; +local SPRITE_SIZE = 24; +local SPRITE_SCALE_FACTOR = SPRITE_SIZE / 256; +local SPRITE_OFFSET = 128; +local MIN_ARC_SIZE = SPRITE_SIZE; local FORCE_SPRING = -0.005; local FORCE_CHARGE = 10000000; @@ -102,10 +104,8 @@ function Node.new(parent, path, name, x, y, spritebatch) -- blueprint of how the files need to be arranged. -- local function createOnionLayers(count) - local MIN_ARC_SIZE = 15; - local fileCounter = 0; - local radius = -15; -- Radius of the circle around the node. + local radius = -SPRITE_SIZE; -- Radius of the circle around the node. local layers = { { radius = radius, amount = fileCounter } }; @@ -121,7 +121,7 @@ function Node.new(parent, path, name, x, y, spritebatch) -- of the current layer and the number of nodes that can be placed -- on that layer and move to the next layer. if arc < MIN_ARC_SIZE then - radius = radius + 15; + radius = radius + SPRITE_SIZE; -- Create a new layer. layers[#layers + 1] = { radius = radius, amount = 1 }; @@ -139,13 +139,23 @@ function Node.new(parent, path, name, x, y, spritebatch) -- @param files -- local function plotCircle(files, count) + -- Sort files based on their extension before placing them. + local toSort = {}; + for _, file in pairs(files) do + toSort[#toSort + 1] = { extension = file:getExtension(), file = file }; + end + table.sort(toSort, function(a, b) + return a.extension > b.extension; + end) + -- Get a blueprint of how the file nodes need to be distributed amongst different layers. local layers, maxradius = createOnionLayers(count); -- Update the position of the file nodes based on the previously calculated onion-layers. local fileCounter = 0; local layer = 1; - for _, file in pairs(files) do + for i = 1, #toSort do + local file = toSort[i].file; fileCounter = fileCounter + 1; -- If we have more files on the current layer than allowed, we "move" @@ -222,7 +232,7 @@ function Node.new(parent, path, name, x, y, spritebatch) file:update(dt); file:setPosition(posX, posY); spritebatch:setColor(file:getColor()); - spritebatch:add(file:getX(), file:getY(), 0, SPRITE_SIZE, SPRITE_SIZE, SPRITE_OFFSET, SPRITE_OFFSET); + spritebatch:add(file:getX(), file:getY(), 0, SPRITE_SCALE_FACTOR, SPRITE_SCALE_FACTOR, SPRITE_OFFSET, SPRITE_OFFSET); end return posX, posY; end @@ -329,7 +339,7 @@ function Node.new(parent, path, name, x, y, spritebatch) end function self:getMass() - return 0.01 * (childCount + math.log(math.max(15, radius))); + return 0.015 * (childCount + math.log(math.max(SPRITE_SIZE, radius))); end function self:isConnectedTo(node) diff --git a/src/logfactory/LogCreator.lua b/src/logfactory/LogCreator.lua new file mode 100644 index 0000000..997fe07 --- /dev/null +++ b/src/logfactory/LogCreator.lua @@ -0,0 +1,84 @@ +local LogCreator = {}; + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local GIT_COMMAND = 'git -C "' +local LOG_COMMAND = '" log --reverse --numstat --pretty=format:"info: %an|%ae|%ct" --name-status --no-merges'; +local FIRST_COMMIT_COMMAND = '" log --pretty=format:%ct|tail -1'; +local LATEST_COMMIT_COMMAND = '" log --pretty=format:%ct|head -1'; +local TOTAL_COMMITS_COMMAND = '" rev-list HEAD --count'; +local LOG_FOLDER = 'logs/'; +local LOG_FILE = '/log.txt'; +local INFO_FILE = '/info.lua'; + +-- ------------------------------------------------ +-- Public Functions +-- ------------------------------------------------ + +--- +-- Creates a git log if git is available and no log has been +-- created in the target folder yet. +-- @param projectname +-- @param path +-- +function LogCreator.createGitLog(projectname, path, force) + if not force and love.filesystem.isFile(LOG_FOLDER .. projectname .. LOG_FILE) then + io.write('Git log for ' .. projectname .. ' already exists!\r\n'); + else + io.write('Writing log for ' .. projectname .. '.\r\n'); + love.filesystem.createDirectory(LOG_FOLDER .. projectname); + + local cmd = GIT_COMMAND .. path .. LOG_COMMAND; + local handle = io.popen(cmd); + love.filesystem.write(LOG_FOLDER .. projectname .. LOG_FILE, handle:read('*all')); + handle:close(); + io.write('Done!\r\n'); + end +end + +function LogCreator.createInfoFile(projectname, path, force) + if not force and love.filesystem.isFile(LOG_FOLDER .. projectname .. INFO_FILE) then + io.write('Info file for ' .. projectname .. ' already exists!\r\n'); + elseif love.system.getOS() ~= 'Windows' then + local fileContent = ''; + fileContent = fileContent .. 'return {\r\n'; + + -- Project name. + fileContent = fileContent .. ' name = "' .. projectname .. '",\r\n'; + + -- First commit. + local handle = io.popen(GIT_COMMAND .. path .. FIRST_COMMIT_COMMAND); + fileContent = fileContent .. ' firstCommit = ' .. handle:read('*a'):gsub('[%s]+', '') .. ',\r\n'; + handle:close(); + + -- Latest commit. + local handle = io.popen(GIT_COMMAND .. path .. LATEST_COMMIT_COMMAND); + fileContent = fileContent .. ' latestCommit = ' .. handle:read('*a'):gsub('[%s]+', '') .. ',\r\n'; + handle:close(); + + -- Number of commits. + local handle = io.popen(GIT_COMMAND .. path .. TOTAL_COMMITS_COMMAND); + fileContent = fileContent .. ' totalCommits = ' .. handle:read('*a'):gsub('[%s]+', '') .. '\r\n'; + handle:close(); + + fileContent = fileContent .. '};\r\n'; + + love.filesystem.write(LOG_FOLDER .. projectname .. INFO_FILE, fileContent); + end +end + +-- ------------------------------------------------ +-- Getters +-- ------------------------------------------------ + +--- +-- Checks if git is available on the system. +-- +function LogCreator.isGitAvailable() + return os.execute('git version') == 0; +end + + +return LogCreator; diff --git a/src/logfactory/LogLoader.lua b/src/logfactory/LogLoader.lua new file mode 100644 index 0000000..2112abd --- /dev/null +++ b/src/logfactory/LogLoader.lua @@ -0,0 +1,243 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local LogLoader = {}; + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local LOG_FOLDER = 'logs'; +local LOG_FILE = 'log.txt'; +local INFO_FILE = 'info.lua'; + +local TAG_INFO = 'info: '; +local ROOT_FOLDER = 'root'; + +local WARNING_TITLE = 'No git log found.'; +local WARNING_MESSAGE = [[ +Looks like you are using LoGiVi for the first time. An example git log has been created in the save directory. Press 'Yes' to open the save directory. + +Press 'Show Help' to view the wiki (online) for more information on how to generate a proper log. + +Press 'No' to proceed to the selection screen from where you can view the example project. +]]; + +local EXAMPLE_TEMPLATE_PATH = 'res/templates/example_log.txt'; +local EXAMPLE_TARGET_PATH = 'logs/example/'; + +-- ------------------------------------------------ +-- Local variables +-- ------------------------------------------------ + +local list; + +-- ------------------------------------------------ +-- Local Functions +-- ------------------------------------------------ + +--- +-- Remove the specified tag from the line. +-- @param line +-- @param tag +-- +local function removeTag(line, tag) + return line:gsub(tag, ''); +end + +--- +-- @param author +-- +local function splitLine(line, delimiter) + local tmp = {} + for part in line:gmatch('[^' .. delimiter .. ']+') do + tmp[#tmp + 1] = part; + end + return tmp; +end + +--- +-- Creates a list of all folders found in the LOG_FOLDER directory, which +-- contain a LOG_FILE. Returns a sequence which contains the names of the folders +-- and the path to the log files in those folders. +-- @param dir +-- +local function fetchProjectFolders(dir) + local folders = {}; + + for _, name in ipairs(love.filesystem.getDirectoryItems(dir)) do + local subdir = dir .. '/' .. name; + if love.filesystem.isDirectory(subdir) and love.filesystem.isFile(subdir .. '/' .. LOG_FILE) then + folders[#folders + 1] = { name = name, path = subdir .. '/' .. LOG_FILE }; + end + end + + return folders; +end + +--- +-- Reads the whole log file and stores each line in a sequence. +-- @param path +-- +local function parseLog(path) + local file = {}; + for line in love.filesystem.lines(path) do + if line ~= '' then + file[#file + 1] = line; + end + end + return file; +end + +--- +-- Turns a unix timestamp into a human readable date string. +-- @param timestamp +-- +local function createDateFromUnixTimestamp(timestamp) + local date = os.date('*t', tonumber(timestamp)); + return string.format("%02d:%02d:%02d - %02d-%02d-%04d", date.hour, date.min, date.sec, date.day, date.month, date.year); +end + +--- +-- Splits the log table into commits. Each commit is a new nested table. +-- @param log +-- +local function splitCommits(log) + local commits = {}; + local index = 0; + for i = 1, #log do + local line = log[i]; + + if line:find(TAG_INFO) then + index = index + 1; + commits[index] = {}; + + local info = splitLine(removeTag(line, TAG_INFO), '|'); + commits[index].author, commits[index].email, commits[index].date = info[1], info[2], info[3]; + + -- Transform unix timestamp to a table containing a human-readable date. + commits[index].date = createDateFromUnixTimestamp(commits[index].date); + elseif commits[index] then + -- Split the whole change line into modifier, file name and file path fields. + local path = line:gsub("^(%a)%s*", ''); -- Remove modifier and whitespace. + local file = path:match("/?([^/]+)$"); -- Get the the filename at the end. + path = path:gsub("/?([^/]+)$", ''); -- Remove the filename from the path. + if path ~= '' then + path = '/' .. path; + end + commits[index][#commits[index] + 1] = { modifier = line:sub(1, 1), path = ROOT_FOLDER .. path, file = file }; + end + end + + return commits; +end + +--- +-- Returns the index of a stored log if it can be found. +-- @param name +-- +local function searchLog(name) + for i, log in ipairs(list) do + if log.name == name then + return i; + end + end +end + +--- +-- Checks if the log folder exists and if it is empty or not. +-- +local function hasLogs() + return (love.filesystem.isDirectory('logs') and #list ~= 0); +end + +--- +-- Displays a warning message for the user which gives him the option +-- to open the wiki page or the folder in which the logs need to be placed. +-- +local function showWarning() + local buttons = { "Yes", "No", "Show Help (Online)", enterbutton = 1, escapebutton = 2 }; + + local pressedbutton = love.window.showMessageBox(WARNING_TITLE, WARNING_MESSAGE, buttons, 'warning', false); + if pressedbutton == 1 then + love.system.openURL('file://' .. love.filesystem.getSaveDirectory() .. '/logs'); + elseif pressedbutton == 3 then + love.system.openURL('https://github.com/rm-code/logivi/wiki#instructions'); + end +end + +--- +-- Write an example log file to the save directory. +-- +local function createExample() + love.filesystem.createDirectory(EXAMPLE_TARGET_PATH); + if not love.filesystem.isFile(EXAMPLE_TARGET_PATH .. LOG_FILE) then + local example = love.filesystem.read(EXAMPLE_TEMPLATE_PATH); + love.filesystem.write(EXAMPLE_TARGET_PATH .. LOG_FILE, example); + end +end + +-- ------------------------------------------------ +-- Public Functions +-- ------------------------------------------------ + +--- +-- Try to load a certain log stored in the list. +-- +function LogLoader.load(log) + local index = searchLog(log); + local rawLog = parseLog(list[index].path); + return splitCommits(rawLog); +end + +--- +-- Loads information about a git repository. +-- @param name +-- +function LogLoader.loadInfo(name) + if love.filesystem.isFile(LOG_FOLDER .. '/' .. name .. '/' .. INFO_FILE) then + local info = love.filesystem.load(LOG_FOLDER .. '/' .. name .. '/' .. INFO_FILE)() + info.firstCommit = createDateFromUnixTimestamp(info.firstCommit); + info.latestCommit = createDateFromUnixTimestamp(info.latestCommit); + return info; + end + return { name = name }; +end + +--- +-- Initialises the LogLoader. It will fetch a list of all folders +-- containing a log file. If the list is empty it will display a +-- warning to the user. +-- +function LogLoader.init() + list = fetchProjectFolders(LOG_FOLDER); + + if not hasLogs() then + createExample(); + showWarning(); + list = fetchProjectFolders(LOG_FOLDER); + end + + return list; +end + +return LogLoader; diff --git a/src/LogReader.lua b/src/logfactory/LogReader.lua similarity index 70% rename from src/LogReader.lua rename to src/logfactory/LogReader.lua index 93e77b0..ecd3017 100644 --- a/src/LogReader.lua +++ b/src/logfactory/LogReader.lua @@ -20,10 +20,6 @@ -- THE SOFTWARE. = --================================================================================================== -local TAG_AUTHOR = 'author: '; -local TAG_DATE = 'date: '; -local ROOT_FOLDER = 'root'; - local EVENT_NEW_COMMIT = 'NEW_COMMIT'; local EVENT_CHANGED_FILE = 'LOGREADER_CHANGED_FILE'; @@ -37,15 +33,6 @@ local LogReader = {}; -- Constants -- ------------------------------------------------ -local WARNING_TITLE = 'No git log found.'; -local WARNING_MESSAGE = [[ -To use LoGiVi you will have to generate a git log first. - -You can view the wiki (online) for more information on how to generate a proper log. - -LoGiVi now will open the file directory in which to place the log. -]]; - local MOD_ADD = 'A'; local MOD_DELETE = 'D'; @@ -65,27 +52,6 @@ local observers; -- Local Functions -- ------------------------------------------------ ---- --- Remove the specified tag from the line. --- @param line --- @param tag --- -local function removeTag(line, tag) - return line:gsub(tag, ''); -end - ---- --- @param author --- -local function splitLine(line, delimiter) - local pos = line:find(delimiter); - if pos then - return line:sub(1, pos - 1), line:sub(pos + 1); - else - return line; - end -end - --- -- Notify observers about the event. -- @param event @@ -97,43 +63,6 @@ local function notify(event, ...) end end ---- --- Split up the log table into commits. Each commit is a new --- nested table. --- @param log --- -local function splitCommits(log) - local commits = {}; - local index = 0; - for i = 1, #log do - local line = log[i]; - - if line:find(TAG_AUTHOR) then - index = index + 1; - commits[index] = {}; - commits[index].author, commits[index].email = splitLine(removeTag(line, TAG_AUTHOR), '|'); - elseif line:find(TAG_DATE) then - -- Transform unix timestamp to a table containing a human-readable date. - local timestamp = removeTag(line, TAG_DATE); - local date = os.date('*t', tonumber(timestamp)); - commits[index].date = string.format("%02d:%02d:%02d - %02d-%02d-%04d", - date.hour, date.min, date.sec, - date.day, date.month, date.year); - elseif commits[index] then - -- Split the whole change line into modifier, file name and file path fields. - local path = line:gsub("^(%a)%s*", ''); -- Remove modifier and whitespace. - local file = path:match("/?([^/]+)$"); -- Get the the filename at the end. - path = path:gsub("/?([^/]+)$", ''); -- Remove the filename from the path. - if path ~= '' then - path = '/' .. path; - end - commits[index][#commits[index] + 1] = { modifier = line:sub(1, 1), path = ROOT_FOLDER .. path, file = file }; - end - end - - return commits; -end - --- -- This function will take a git modifier and return the direct -- opposite of it. @@ -148,41 +77,6 @@ local function reverseGitStatus(modifier) return modifier; end ---- --- Checks if there is a log file LoGiVi can work with. If the file --- can't be found it will display a warning message and open the save --- folder. --- @param name --- -local function isLogFile(name) - if not love.filesystem.isFile(name) then - local buttons = { "Yes", "No", "Show Help (Online)", enterbutton = 1, escapebutton = 2 }; - - local pressedbutton = love.window.showMessageBox(WARNING_TITLE, WARNING_MESSAGE, buttons, 'warning', false); - if pressedbutton == 1 then - love.system.openURL('file://' .. love.filesystem.getSaveDirectory()); - elseif pressedbutton == 3 then - love.system.openURL('https://github.com/rm-code/logivi/wiki#instructions'); - end - return false; - end - return true; -end - ---- --- Reads the git log and returns it as a table. --- @param path --- -local function readLogFile(path) - local file = {}; - for line in love.filesystem.lines(path) do - if line ~= '' then - file[#file + 1] = line; - end - end - return file; -end - local function applyNextCommit() if index == #log then return; @@ -269,12 +163,8 @@ end -- Loads the file and stores it line for line in a lua table. -- @param logpath -- -function LogReader.init(logpath, delay, playmode, autoplay) - if isLogFile(logpath) then - log = splitCommits(readLogFile(logpath)); - else - log = {}; - end +function LogReader.init(gitlog, delay, playmode, autoplay) + log = gitlog; -- Set default values. index = 0; diff --git a/src/screens/MainScreen.lua b/src/screens/MainScreen.lua index 0627514..dfbf144 100644 --- a/src/screens/MainScreen.lua +++ b/src/screens/MainScreen.lua @@ -20,42 +20,23 @@ -- THE SOFTWARE. = --================================================================================================== +local ScreenManager = require('lib.screenmanager.ScreenManager'); local Screen = require('lib.screenmanager.Screen'); -local LogReader = require('src.LogReader'); -local Camera = require('lib.camera.Camera'); +local LogReader = require('src.logfactory.LogReader'); +local LogLoader = require('src.logfactory.LogLoader'); +local Camera = require('src.ui.CamWrapper'); local ConfigReader = require('src.conf.ConfigReader'); local AuthorManager = require('src.AuthorManager'); local FileManager = require('src.FileManager'); local Graph = require('src.graph.Graph'); local Panel = require('src.ui.Panel'); local Timeline = require('src.ui.Timeline'); - --- ------------------------------------------------ --- Constants --- ------------------------------------------------ - -local LOG_FILE = 'log.txt'; - -local CAMERA_ROTATION_SPEED = 0.6; -local CAMERA_TRANSLATION_SPEED = 400; -local CAMERA_TRACKING_SPEED = 2; -local CAMERA_ZOOM_SPEED = 0.6; -local CAMERA_MAX_ZOOM = 0.05; -local CAMERA_MIN_ZOOM = 2; +local InputHandler = require('src.InputHandler'); -- ------------------------------------------------ -- Controls -- ------------------------------------------------ -local camera_zoomIn; -local camera_zoomOut; -local camera_rotateL; -local camera_rotateR; -local camera_n; -local camera_s; -local camera_e; -local camera_w; - local toggleAuthors; local toggleFilePanel; local toggleLabels; @@ -68,6 +49,8 @@ local loadPrevCommit; local toggleFullscreen; +local exit; + -- ------------------------------------------------ -- Module -- ------------------------------------------------ @@ -82,87 +65,21 @@ function MainScreen.new() local self = Screen.new(); local graph; - local camera; - local cx, cy; - local ox, oy; - local zoom = 1; - local filePanel; local timeline; + local log; -- ------------------------------------------------ -- Private Functions -- ------------------------------------------------ - --- - -- Processes camera related controls and updates the camera. - -- @param cx - The current x-position the camera is looking at. - -- @param cy - The current y-position the camera is looking at. - -- @param ox - The current offset of the camera on the x-axis. - -- @param oy - The current offset of the camera on the y-axis. - -- @param dt - -- - local function updateCamera(cx, cy, ox, oy, dt) - -- Zoom. - if love.keyboard.isDown(camera_zoomIn) then - zoom = zoom + CAMERA_ZOOM_SPEED * dt; - elseif love.keyboard.isDown(camera_zoomOut) then - zoom = zoom - CAMERA_ZOOM_SPEED * dt; - end - zoom = math.max(CAMERA_MAX_ZOOM, math.min(zoom, CAMERA_MIN_ZOOM)); - camera:zoomTo(zoom); - - -- Rotation. - if love.keyboard.isDown(camera_rotateL) then - camera:rotate(CAMERA_ROTATION_SPEED * dt); - elseif love.keyboard.isDown(camera_rotateR) then - camera:rotate(-CAMERA_ROTATION_SPEED * dt); - end - - -- Horizontal Movement. - local dx = 0; - if love.keyboard.isDown(camera_w) then - dx = dx - dt * CAMERA_TRANSLATION_SPEED; - elseif love.keyboard.isDown(camera_e) then - dx = dx + dt * CAMERA_TRANSLATION_SPEED; - end - -- Vertical Movement. - local dy = 0; - if love.keyboard.isDown(camera_n) then - dy = dy - dt * CAMERA_TRANSLATION_SPEED; - elseif love.keyboard.isDown(camera_s) then - dy = dy + dt * CAMERA_TRANSLATION_SPEED; - end - - -- Take the camera rotation into account when calculating the new offset. - ox = ox + (math.cos(-camera.rot) * dx - math.sin(-camera.rot) * dy); - oy = oy + (math.sin(-camera.rot) * dx + math.cos(-camera.rot) * dy); - - -- Gradually move the camera to the target position. - local gx, gy = graph:getCenter(); - cx = cx - (cx - math.floor(gx + ox)) * dt * CAMERA_TRACKING_SPEED; - cy = cy - (cy - math.floor(gy + oy)) * dt * CAMERA_TRACKING_SPEED; - camera:lookAt(cx, cy); - - return cx, cy, ox, oy; - end - --- -- Assigns keybindings loaded from the config file to a -- local variable for faster access. -- @param config -- local function assignKeyBindings(config) - camera_zoomIn = config.keyBindings.camera_zoomIn; - camera_zoomOut = config.keyBindings.camera_zoomOut; - camera_rotateL = config.keyBindings.camera_rotateL; - camera_rotateR = config.keyBindings.camera_rotateR; - camera_n = config.keyBindings.camera_n; - camera_s = config.keyBindings.camera_s; - camera_e = config.keyBindings.camera_e; - camera_w = config.keyBindings.camera_w; - toggleAuthors = config.keyBindings.toggleAuthors; toggleFilePanel = config.keyBindings.toggleFileList; toggleLabels = config.keyBindings.toggleLabels; @@ -174,62 +91,55 @@ function MainScreen.new() loadPrevCommit = config.keyBindings.loadPrevCommit; toggleFullscreen = config.keyBindings.toggleFullscreen; - end - - local function setWindowMode(options) - local w, h, flags = love.window.getMode(); - - flags.fullscreen = options.fullscreen; - flags.fullscreentype = options.fullscreenType; - flags.vsync = options.vsync; - flags.msaa = options.msaa; - flags.display = options.display; - flags.x = 0; - flags.y = 0; - love.window.setMode(options.screenWidth, options.screenHeight, flags); + exit = config.keyBindings.exit; end -- ------------------------------------------------ -- Public Functions -- ------------------------------------------------ - function self:init() + function self:init(param) local config = ConfigReader.init(); -- Load keybindings. assignKeyBindings(config); - -- Set the background color based on the option in the config file. - love.graphics.setBackgroundColor(config.options.backgroundColor); - setWindowMode(config.options); - AuthorManager.init(config.aliases, config.avatars, config.options.showAuthors); + -- Create the camera. + camera = Camera.new(); + camera:assignKeyBindings(config); + + -- Load custom colors. + FileManager.setColorTable(config.colors); + graph = Graph.new(config.options.edgeWidth, config.options.showLabels); graph:register(AuthorManager); + graph:register(camera); + + -- Store the name of the currently displayed log. + log = param.log; -- Initialise LogReader and register observers. - LogReader.init(LOG_FILE, config.options.commitDelay, config.options.mode, config.options.autoplay); + LogReader.init(LogLoader.load(log), config.options.commitDelay, config.options.mode, config.options.autoplay); LogReader.register(AuthorManager); LogReader.register(graph); - -- Create the camera. - camera = Camera.new(); - cx, cy = 0, 0; - ox, oy = 0, 0; - -- Create panel. filePanel = Panel.new(0, 0, 150, 400); filePanel:setVisible(config.options.showFileList); timeline = Timeline.new(config.options.showTimeline, LogReader.getTotalCommits(), LogReader.getCurrentDate()); + + -- Run one complete cycle of garbage collection. + collectgarbage('collect'); end function self:draw() camera:draw(function() - graph:draw(camera.rot); - AuthorManager.drawLabels(camera.rot); + graph:draw(camera:getRotation()); + AuthorManager.drawLabels(camera:getRotation()); end); filePanel:draw(FileManager.draw); @@ -247,7 +157,11 @@ function MainScreen.new() timeline:setCurrentCommit(LogReader.getCurrentIndex()); timeline:setCurrentDate(LogReader.getCurrentDate()); - cx, cy, ox, oy = updateCamera(cx, cy, ox, oy, dt); + camera:move(dt); + end + + function self:close() + FileManager.reset(); end function self:quit() @@ -257,24 +171,26 @@ function MainScreen.new() end function self:keypressed(key) - if key == toggleAuthors then + if InputHandler.isPressed(key, toggleAuthors) then AuthorManager.setVisible(not AuthorManager.isVisible()); - elseif key == toggleFilePanel then + elseif InputHandler.isPressed(key, toggleFilePanel) then filePanel:setVisible(not filePanel:isVisible()); - elseif key == toggleLabels then + elseif InputHandler.isPressed(key, toggleLabels) then graph:toggleLabels(); - elseif key == toggleSimulation then + elseif InputHandler.isPressed(key, toggleSimulation) then LogReader.toggleSimulation(); - elseif key == toggleRewind then + elseif InputHandler.isPressed(key, toggleRewind) then LogReader.toggleRewind(); - elseif key == loadNextCommit then + elseif InputHandler.isPressed(key, loadNextCommit) then LogReader.loadNextCommit(); - elseif key == loadPrevCommit then + elseif InputHandler.isPressed(key, loadPrevCommit) then LogReader.loadPrevCommit(); - elseif key == toggleFullscreen then + elseif InputHandler.isPressed(key, toggleFullscreen) then love.window.setFullscreen(not love.window.getFullscreen()); - elseif key == toggleTimeline then + elseif InputHandler.isPressed(key, toggleTimeline) then timeline:toggle(); + elseif InputHandler.isPressed(key, exit) then + ScreenManager.switch('selection', { log = log }); end end @@ -295,6 +211,10 @@ function MainScreen.new() filePanel:mousemoved(x, y, dx, dy); end + function self:resize(nx, ny) + timeline:resize(nx, ny); + end + return self; end diff --git a/src/screens/SelectionScreen.lua b/src/screens/SelectionScreen.lua new file mode 100644 index 0000000..2fe3023 --- /dev/null +++ b/src/screens/SelectionScreen.lua @@ -0,0 +1,167 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local Screen = require('lib.screenmanager.Screen'); +local LogCreator = require('src.logfactory.LogCreator'); +local LogLoader = require('src.logfactory.LogLoader'); +local Tooltip = require('src.ui.Tooltip'); +local Button = require('src.ui.Button'); +local ButtonList = require('src.ui.ButtonList'); +local InfoPanel = require('src.ui.InfoPanel'); +local ConfigReader = require('src.conf.ConfigReader'); +local InputHandler = require('src.InputHandler'); + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local SelectionScreen = {}; + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +function SelectionScreen.new() + local self = Screen.new(); + + local config; + local logList; + local buttonList; + local saveDirButton; + local infoPanel; + + local uiElementPadding = 20; + local uiElementMargin = 5; + + -- ------------------------------------------------ + -- Private Functions + -- ------------------------------------------------ + + --- + -- Updates the project's window settings based on the config file. + -- @param options + -- + local function setWindowMode(options) + local _, _, flags = love.window.getMode(); + + flags.fullscreen = options.fullscreen; + flags.fullscreentype = options.fullscreenType; + flags.vsync = options.vsync; + flags.msaa = options.msaa; + flags.display = options.display; + + love.window.setMode(options.screenWidth, options.screenHeight, flags); + + local sw, sh = love.window.getDesktopDimensions(); + love.window.setPosition(sw * 0.5 - love.graphics.getWidth() * 0.5, sh * 0.5 - love.graphics.getHeight() * 0.5); + end + + -- ------------------------------------------------ + -- Public Functions + -- ------------------------------------------------ + + function self:init(param) + config = ConfigReader.init(); + + -- Set the background color based on the option in the config file. + love.graphics.setBackgroundColor(config.options.backgroundColor); + setWindowMode(config.options); + + -- Create git logs for repositories specified in the config file. + if LogCreator.isGitAvailable() then + for name, path in pairs(config.repositories) do + LogCreator.createGitLog(name, path); + LogCreator.createInfoFile(name, path); + end + end + + -- Intitialise LogLoader. + logList = LogLoader.init(); + + -- A scrollable list of buttons which can be used to select a certain log. + buttonList = ButtonList.new(uiElementPadding, uiElementPadding, uiElementMargin); + buttonList:init(logList); + + -- The info panel which displays more information about a selected log. + infoPanel = InfoPanel.new(uiElementPadding + (2 * uiElementMargin) + buttonList:getButtonWidth(), uiElementPadding); + infoPanel:setInfo(LogLoader.loadInfo(param and param.log or logList[1].name)); + + -- Create a button which opens the save directory. + saveDirButton = Button.new('', uiElementPadding - 10, love.graphics.getHeight() - uiElementPadding - 10, uiElementPadding, uiElementPadding); + saveDirButton:setTooltip(Tooltip.new('Opens the save directory', 10, 10, 180, 40)); + end + + function self:update(dt) + buttonList:update(dt); + infoPanel:update(dt); + saveDirButton:update(dt, love.mouse.getPosition()); + end + + function self:resize(x, y) + infoPanel:resize(x, y); + saveDirButton:setPosition(uiElementPadding - 10, y - uiElementPadding - 10); + end + + function self:draw() + buttonList:draw(); + infoPanel:draw(); + saveDirButton:draw(); + love.graphics.print('Work in Progress (v' .. getVersion() .. ')', love.graphics.getWidth() - 180, love.graphics.getHeight() - uiElementPadding); + end + + function self:mousepressed(x, y, b) + local logId = buttonList:pressed(x, y, b); + if logId then + infoPanel:setInfo(LogLoader.loadInfo(logId)); + end + + logId = infoPanel:pressed(x, y, b); + if logId and LogCreator.isGitAvailable() and config.repositories[logId] then + local forceOverwrite = true; + LogCreator.createGitLog(logId, config.repositories[logId], forceOverwrite); + LogCreator.createInfoFile(logId, config.repositories[logId], forceOverwrite); + infoPanel:setInfo(LogLoader.loadInfo(logId)); + end + + if saveDirButton:hasFocus() then + love.system.openURL('file://' .. love.filesystem.getSaveDirectory()); + end + end + + function self:keypressed(key) + if InputHandler.isPressed(key, config.keyBindings.exit) then + love.event.quit(); + elseif InputHandler.isPressed(key, config.keyBindings.toggleFullscreen) then + love.window.setFullscreen(not love.window.getFullscreen()); + end + end + + function self:quit() + if ConfigReader.getConfig('options').removeTmpFiles then + ConfigReader.removeTmpFiles(); + end + end + + return self; +end + +return SelectionScreen; diff --git a/src/ui/Button.lua b/src/ui/Button.lua new file mode 100644 index 0000000..e795faf --- /dev/null +++ b/src/ui/Button.lua @@ -0,0 +1,108 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local Button = {}; + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local LABEL_FONT = love.graphics.newFont('res/fonts/SourceCodePro-Medium.otf', 20); +local DEFAULT_FONT = love.graphics.newFont(12); + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +function Button.new(id, x, y, w, h) + local self = {}; + + local focus; + local focusTimer = 0; + local col = { 100, 100, 100, 100 }; + local hlcol = { 150, 150, 150, 150 }; + + local offsetX, offsetY = 0, 0; + + local tooltip; + + -- ------------------------------------------------ + -- Private Functions + -- ------------------------------------------------ + + function self:draw() + love.graphics.setFont(LABEL_FONT); + love.graphics.setColor(focus and hlcol or col); + love.graphics.rectangle('fill', x + offsetX, y + offsetY, w, h); + love.graphics.setColor(255, 255, 255, 100); + love.graphics.rectangle('line', x + offsetX, y + offsetY, w, h); + love.graphics.print(id, x + offsetX + 10, y + offsetY + 10); + love.graphics.setFont(DEFAULT_FONT); + love.graphics.setColor(255, 255, 255, 255); + + if focus and (tooltip and focusTimer > 0.3) then + tooltip:draw(); + end + end + + function self:update(dt, mx, my) + focus = x + offsetX < mx and x + w > mx + offsetX and y + offsetY < my and y + offsetY + h > my; + + focusTimer = focus and focusTimer + dt or 0; + + if tooltip then + tooltip:update(mx, my); + end + end + + -- ------------------------------------------------ + -- Getters + -- ------------------------------------------------ + + function self:getId() + return id; + end + + function self:hasFocus() + return focus; + end + + -- ------------------------------------------------ + -- Setters + -- ------------------------------------------------ + + function self:setOffset(nox, noy) + offsetX, offsetY = nox, noy; + end + + function self:setPosition(nx, ny) + x, y = nx, ny; + end + + function self:setTooltip(ntooltip) + tooltip = ntooltip; + end + + return self; +end + +return Button; diff --git a/src/ui/ButtonList.lua b/src/ui/ButtonList.lua new file mode 100644 index 0000000..b75719b --- /dev/null +++ b/src/ui/ButtonList.lua @@ -0,0 +1,116 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local Button = require('src.ui.Button'); + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local ButtonList = {}; + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +function ButtonList.new(offsetX, offsetY, margin) + local self = {}; + + local buttons; + + local scrollOffset = 0; + local scrollSpeed = 20; + local buttonW = 200; + local buttonH = 40; + local listLength = 0; + + -- ------------------------------------------------ + -- Public Functions + -- ------------------------------------------------ + + function self:init(logList) + buttons = {}; + for i, log in ipairs(logList) do + buttons[#buttons + 1] = Button.new(log.name, offsetX, offsetY + (i - 1) * (buttonH) + margin * (i - 1), buttonW, buttonH); + end + + listLength = listLength + offsetY + (#buttons - 1) * (buttonH) + margin * (#buttons - 1); + end + + function self:draw() + love.graphics.setScissor(offsetX, offsetY, buttonW, love.graphics.getHeight() - offsetY * 3); + for _, button in ipairs(buttons) do + button:draw(scrollOffset); + end + love.graphics.setScissor(); + end + + function self:update(dt) + local mx, my = love.mouse.getPosition(); + for _, button in ipairs(buttons) do + button:setOffset(0, scrollOffset); + button:update(dt, mx, my); + end + end + + function self:scroll(mx, my, scrollFactor) + -- Deactivate scrolling if the list is smaller than the screen. + if listLength < love.graphics.getHeight() - offsetY * 2 then + return; + end + + if offsetX < mx and offsetX + buttonW > mx then + scrollOffset = scrollOffset + scrollFactor; + + if scrollOffset >= 0 then + -- Stop at top of the list. + scrollOffset = 0; + elseif (scrollOffset + listLength) <= (love.graphics.getHeight() - offsetY * 2 - buttonH) then + -- Stop at bottom of the list. + scrollOffset = love.graphics.getHeight() - offsetY * 2 - buttonH - listLength; + end + end + end + + function self:pressed(x, y, b) + if b == 'wu' then + self:scroll(x, y, -scrollSpeed); + elseif b == 'wd' then + self:scroll(x, y, scrollSpeed); + elseif b == 'l' then + for _, button in ipairs(buttons) do + if button:hasFocus() then + print('Select log: ' .. button:getId()); + return button:getId(); + end + end + end + end + + function self:getButtonWidth() + return buttonW; + end + + return self; +end + +return ButtonList; diff --git a/src/ui/CamWrapper.lua b/src/ui/CamWrapper.lua new file mode 100644 index 0000000..fd8bd0a --- /dev/null +++ b/src/ui/CamWrapper.lua @@ -0,0 +1,175 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local InputHandler = require('src.InputHandler'); +local Camera = require('lib.camera.Camera'); + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local CamWrapper = {}; + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local CAMERA_ROTATION_SPEED = 0.6; +local CAMERA_TRANSLATION_SPEED = 400; +local CAMERA_TRACKING_SPEED = 2; +local CAMERA_ZOOM_SPEED = 0.6; +local CAMERA_MAX_ZOOM = 0.05; +local CAMERA_MIN_ZOOM = 2; + +-- ------------------------------------------------ +-- Local variables +-- ------------------------------------------------ + +local camera_zoomIn; +local camera_zoomOut; +local camera_rotateL; +local camera_rotateR; +local camera_n; +local camera_s; +local camera_e; +local camera_w; + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +function CamWrapper.new() + local self = {}; + + local camera = Camera.new(); -- The actual camera object. + local cx, cy = 0, 0; + local ox, oy = 0, 0; + local gx, gy = 0, 0; + local zoom = 1; + + -- ------------------------------------------------ + -- Private Functions + -- ------------------------------------------------ + + --- + -- Updates the position on which the camera offset builds. + -- @param ngx + -- @param ngy + -- + local function updateCenter(ngx, ngy) + gx, gy = ngx, ngy; + end + + -- ------------------------------------------------ + -- Public Functions + -- ------------------------------------------------ + + --- + -- Assign the keybindings to local variables for faster access. + -- @param config + -- + function self:assignKeyBindings(config) + camera_zoomIn = config.keyBindings.camera_zoomIn; + camera_zoomOut = config.keyBindings.camera_zoomOut; + camera_rotateL = config.keyBindings.camera_rotateL; + camera_rotateR = config.keyBindings.camera_rotateR; + camera_n = config.keyBindings.camera_n; + camera_s = config.keyBindings.camera_s; + camera_e = config.keyBindings.camera_e; + camera_w = config.keyBindings.camera_w; + end + + --- + -- Processes camera related controls and updates the camera. + -- @param dt + -- + function self:move(dt) + -- Zoom. + if InputHandler.isDown(camera_zoomIn) then + zoom = zoom + CAMERA_ZOOM_SPEED * dt; + elseif InputHandler.isDown(camera_zoomOut) then + zoom = zoom - CAMERA_ZOOM_SPEED * dt; + end + zoom = math.max(CAMERA_MAX_ZOOM, math.min(zoom, CAMERA_MIN_ZOOM)); + camera:zoomTo(zoom); + + -- Rotation. + if InputHandler.isDown(camera_rotateL) then + camera:rotate(CAMERA_ROTATION_SPEED * dt); + elseif InputHandler.isDown(camera_rotateR) then + camera:rotate(-CAMERA_ROTATION_SPEED * dt); + end + + -- Horizontal Movement. + local dx = 0; + if InputHandler.isDown(camera_w) then + dx = dx - dt * CAMERA_TRANSLATION_SPEED; + elseif InputHandler.isDown(camera_e) then + dx = dx + dt * CAMERA_TRANSLATION_SPEED; + end + -- Vertical Movement. + local dy = 0; + if InputHandler.isDown(camera_n) then + dy = dy - dt * CAMERA_TRANSLATION_SPEED; + elseif InputHandler.isDown(camera_s) then + dy = dy + dt * CAMERA_TRANSLATION_SPEED; + end + + -- Take the camera rotation into account when calculating the new offset. + ox = ox + (math.cos(-camera.rot) * dx - math.sin(-camera.rot) * dy); + oy = oy + (math.sin(-camera.rot) * dx + math.cos(-camera.rot) * dy); + + -- Gradually move the camera to the target position. + cx = cx - (cx - math.floor(gx + ox)) * dt * CAMERA_TRACKING_SPEED; + cy = cy - (cy - math.floor(gy + oy)) * dt * CAMERA_TRACKING_SPEED; + camera:lookAt(cx, cy); + end + + --- + -- @param func + -- + function self:draw(func) + camera:draw(func); + end + + --- + -- Receives events from an observable. + -- @param event + -- @param ... + -- + function self:receive(event, ...) + if event == 'GRAPH_UPDATE_CENTER' then + updateCenter(...); + end + end + + --- + -- Returns the camera's rotation. + -- + function self:getRotation() + return camera.rot; + end + + return self; +end + +return CamWrapper; diff --git a/src/ui/InfoPanel.lua b/src/ui/InfoPanel.lua new file mode 100644 index 0000000..a0e0e03 --- /dev/null +++ b/src/ui/InfoPanel.lua @@ -0,0 +1,114 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local ScreenManager = require('lib.screenmanager.ScreenManager'); +local Button = require('src.ui.Button'); + +-- ------------------------------------------------ +-- Module +-- ------------------------------------------------ + +local InfoPanel = {}; + +-- ------------------------------------------------ +-- Constants +-- ------------------------------------------------ + +local HEADER_FONT = love.graphics.newFont('res/fonts/SourceCodePro-Bold.otf', 35); +local TEXT_FONT = love.graphics.newFont('res/fonts/SourceCodePro-Medium.otf', 15); +local DEFAULT_FONT = love.graphics.newFont(12); + +-- ------------------------------------------------ +-- Constructor +-- ------------------------------------------------ + +function InfoPanel.new(x, y) + local self = {}; + + local info = {}; + local watchButton = Button.new('Watch', love.graphics.getWidth() - 20 - 80 - 5, love.graphics.getHeight() - 85, 80, 40); + local refreshButton = Button.new('Refresh', love.graphics.getWidth() - (20 + 80 + 5) * 2, love.graphics.getHeight() - 85, 100, 40); + + -- ------------------------------------------------ + -- Public Functions + -- ------------------------------------------------ + + function self:update(dt) + local mx, my = love.mouse.getPosition(); + watchButton:update(dt, mx, my); + refreshButton:update(dt, mx, my); + end + + function self:draw() + love.graphics.setColor(100, 100, 100, 100); + love.graphics.rectangle('fill', x, y, love.graphics.getWidth() - x - 20, love.graphics.getHeight() - y - 40); + love.graphics.setColor(255, 255, 255, 100); + love.graphics.rectangle('line', x, y, love.graphics.getWidth() - x - 20, love.graphics.getHeight() - y - 40); + + love.graphics.setFont(HEADER_FONT); + love.graphics.setColor(0, 0, 0, 100); + love.graphics.print(info.name, x + 25, y + 25); + love.graphics.setColor(255, 100, 100, 255); + love.graphics.print(info.name, x + 20, y + 20); + love.graphics.setColor(255, 255, 255, 255); + + love.graphics.setFont(TEXT_FONT); + love.graphics.print('First commit: ' .. info.firstCommit, x + 25, y + 100); + love.graphics.print('Latest commit: ' .. info.latestCommit, x + 25, y + 125); + love.graphics.print('Total commits: ' .. info.totalCommits, x + 25, y + 150); + + love.graphics.setFont(DEFAULT_FONT); + + watchButton:draw(); + refreshButton:draw(); + end + + function self:pressed(x, y, b) + if b == 'l' then + if watchButton:hasFocus() then + ScreenManager.switch('main', { log = info.name }); + elseif refreshButton:hasFocus() then + return info.name; + end + end + end + + function self:resize(nx, ny) + watchButton:setPosition(nx - 20 - 80 - 5, ny - 85); + refreshButton:setPosition(nx - (20 + 80 + 5) * 2, ny - 85); + end + + -- ------------------------------------------------ + -- Setters + -- ------------------------------------------------ + + function self:setInfo(ninfo) + info.name = ninfo.name or ''; + info.firstCommit = ninfo.firstCommit or ''; + info.latestCommit = ninfo.latestCommit or ''; + info.totalCommits = ninfo.totalCommits or ''; + end + + return self; +end + +return InfoPanel; diff --git a/src/ui/Panel.lua b/src/ui/Panel.lua index 39498b0..0d81687 100644 --- a/src/ui/Panel.lua +++ b/src/ui/Panel.lua @@ -1,5 +1,5 @@ --================================================================================================== --- Copyright (C) 2015 by Robert Machmer = +-- Copyright (C) 2015 by Robert Machmer = -- = -- Permission is hereby granted, free of charge, to any person obtaining a copy = -- of this software and associated documentation files (the "Software"), to deal = @@ -200,4 +200,4 @@ function Panel.new(x, y, w, h) return self; end -return Panel; \ No newline at end of file +return Panel; diff --git a/src/ui/Timeline.lua b/src/ui/Timeline.lua index 09af2c2..38f8849 100644 --- a/src/ui/Timeline.lua +++ b/src/ui/Timeline.lua @@ -106,6 +106,10 @@ function Timeline.new(v, totalCommits, date) end end + function self:resize(nx, ny) + stepWidth = (nx - MARGIN_LEFT - MARGIN_RIGHT) / TOTAL_STEPS; + end + return self; end diff --git a/src/ui/Tooltip.lua b/src/ui/Tooltip.lua new file mode 100644 index 0000000..2bce515 --- /dev/null +++ b/src/ui/Tooltip.lua @@ -0,0 +1,48 @@ +--================================================================================================== +-- Copyright (C) 2014 - 2015 by Robert Machmer = +-- = +-- 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. = +--================================================================================================== + +local Tooltip = {}; + +function Tooltip.new(text, x, y, w, h) + local self = {}; + + local col = { 100, 100, 100, 255 }; + + function self:update(mx, my) + x = math.max(0, math.min(mx + 10, love.graphics.getWidth() - w)); + y = math.max(0, math.min(my + 10, love.graphics.getHeight() - h)); + end + + function self:draw() + love.graphics.setColor(col); + love.graphics.rectangle('fill', x, y, w, h); + love.graphics.setColor(255, 255, 255, 100); + love.graphics.rectangle('line', x, y, w, h); + love.graphics.setColor(255, 255, 255, 255); + love.graphics.print(text, x + 10, y + 10); + love.graphics.setColor(255, 255, 255, 255); + end + + return self; +end + +return Tooltip;