luce, 在JUCE上,为基于Lua的一个




  繁體 雙語
Luce - a GUI module for Lua based on JUCE
  • 源代码名称:luce
  • 源代码网址:
  • luce源代码文档
  • luce源代码下载
  • Git URL:
    Git Clone代码到本地:
    git clone
    $ svn co --depth empty
    Checked out revision 1.
    $ cd repo
    $ svn up trunk

    ( 基于JUCE的) 一个用于Lua的用户界面

    Luce 0.3.4有时,它将不会失败,因为已经发布

    下载 Linux,Windows,OS X,iOS和 Android。

    什么是 Luce

    Luce 是一个基于可移植。可以嵌入。杰出的C++ 库 JUCE的用于GUI编程的 Lua MODULE。

    它可以作为纯 MODULE 5.1/lua 5.2/luajit 2. X 脚本或者作为 C++/lua项目的库使用。

    尽管基于 JUCE,Luce并不是为了成为JUCE的绑定,但是不能使用Luce特性的低级 API。

    Luce有 Lua,将为 Lua developpers创建。

    Lua的本质是" a powerfull, fast, lightweight embeddable scripting language. 那么Luce也会。


    目前,它支持 Linux。Windows 和 Mac OS X,对iOS和Android有部分支持。


    • 如果你在Linux上,你还可以使用提供的luarock:


      对于 lua 5.1:

      ~$ luarocks install

      对于 lua 5.2:

      ~$ luarocks install

      对于 lua 5.3:

      ~$ luarocks install
    • 如果不知道 JUCE,应该考虑查看它的文档( 作为低级别 API MATCHES的大多数 JUCE API。

      有关详细说明,请参见与 JUCELuce设计的差异。

    是第一个应用程序: 打招呼

    local title ="Hello World!"local app, luce =require"luce.LApplication"(title, ...) -- create a basic Application-- with command line parameterslocal timer = luce:Timer() -- a timer for the animationlocal function MainWindow(args) -- our main component class-- will receive command line arguments or starting eventslocal app, luce = app, luce -- just to put these in the local environmentlocal Colours = luce.Colours-- id.local wsize = {800,600} -- the size of our windowlocal isPaused =false-- holds the animation statelocal mc = luce:MainComponent("MainComponent") -- our main componentlocal documentWindow = luce:Document(title) -- create a document window to hold the main componentlocal button = luce:TextButton("quit") -- a button to close the app button.buttonText="Quit"-- visible text of the button button:setLookAndFeel(4) -- change look-and-feel button.bounds= { 10, 10, 100, 20 } -- and position it (x,y,w,h)----- set some action on button click--- button:buttonClicked(function()
     ----- say hello to the world with a simple animation----- just a dumb function to increase font size and pick a predefined colourlocal currentSize, baseSize, maxSize =12.0, 12.0, 32.0local colours = {,, Colours.yellow,, }
     local currentCol =1local function changeSize()
     currentSize = (currentSize<maxSize) and currentSize+0.5or baseSize
     currentCol = (currentCol>=#colours) and1or currentCol+1return currentSize, colours[currentCol]
     end-- draw the message mc:paint(function(g)
     -- draw a background g:fillCheckerBoard( luce:Rectangle(mc:getBounds()), 48, 48, Colours.lightgrey, Colours.white)
     -- get new font size and colourlocal size, colour =changeSize()
     -- draw text g:setFont(size)
     g:drawText("Hello World!", mc:getLocalBounds(), luce.JustificationType.centred, true);
     -- animate it a bit via the timer callback timer:timerCallback(function()
     if(mc:isShowing() andnot(isPaused))then mc:repaint()
     timer:startTimer (1000/60)
     ----- add some key shortcuts---local K = string.bytelocal kc =setmetatable(
     luce.KeyPress.KeyCodes, { __index =function()return0end } ) -- just a little trick to always get a valid keycode-- see for-- available key-codes documentWindow:keyPressed(function(k)
     local k, m = k:getKeyCode(), k:getModifiers() -- get current key-code and modifiers statusif (k==K"Q"or k==K"q")
     and (m:isCommandDown() ornot(app.os.osx)) then-- if Q is pressed or, on OS X, cmd+Q app:exit(0) -- close the application with an normal exit state (0 by default)elseif (k==K"w"or k==K"W") and (m:isCommandDown() ) then-- if cmd/ctrl + W is pressed documentWindow:closeWindow() -- close the window-- on Linux and Windows, this has the effect of closing the application too,-- but on OS X, only the active window is closed by default-- however, if true is passed to closeWindow()-- and this is the last remaining Document-- the app will close the window and quit the applicationelseif (k==kc.spaceKey) then-- toggle rendering pause isPaused =not(isPaused)
     elsereturnfalse-- don't consume key-- returning false, nil or not returning anything-- has the same effectendreturntrue-- tell the OS we have consumed this keyend)
     ----- add all components and display--- mc:setSize(wsize)
     mc:addAndMakeVisible(button) -- add the component to our main component documentWindow:setContentOwned( mc, true ) -- add the main component to the document window--- documentWindow:closeButtonPressed(function() -- the user asked to close the current window... documentWindow:closeWindow() -- so let's close it our way-- if this action's not taken, it'll close the app by defaultend)
     documentWindow:setSize(wsize) -- set dimensions for the window-- on iOS and Android, it'll just set a fullscreen documentWindow:setVisible(true) -- display the documentreturn documentWindow -- return it for the application to actually display itendlocal manual =false-- set true if you want to add your own process running along with the main looplocal osx_delayed =false-- set true if you don't want your app to display a window immediatly on OS X-- but wait for user input before, like providing a file,...local poller =function() -- the callback you want to run in manual modeprint"I'm in a loop!"endreturn app:start( MainWindow, manual and {poller,100}, osx_delayed ) -- returns the exit state


    local luce =require"luce"()------ create a default JUCEApplication---local mainWindow = luce:JUCEApplication("My App")------ create a DocumentWindow with name"Document Window"---local dw = luce:DocumentWindow("Document Window")-- rename"Luce Example Application"------ create a MainComponent, to be hosted by the Document Window---local mc = luce:MainComponent("The Main Component")------ create a button named"TheButton" with text"a button"---local button = luce:TextButton("TheButton")
    button:setButtonText( "a button" ) -- or button.buttonText ="a button", like button2 below--- add a callback for when button is clickedbutton:buttonClicked(function(...)
     print("button clicked!!")end)--- change component look and feellocal button2 = luce:TextButton("TheButton2")
    button2.buttonText="button with a different look and feel"--- add a callback for when button is clickedbutton2:buttonClicked(function(...)
     print("button 2 clicked!!")end)--- set a different look and feel for button2button2:setLookAndFeel(3)------ create a Label---local label = luce:Label("A Label")--- Label's setText has an optional parameter to send a notification when its content's changed--- by default, it sends nothing:label.text="a bit of content"-- default to dontSendNotification--- set a callback for label text changeslabel:labelTextChanged(function(...)
     print("Label text has changed: ", label:getText()) -- or label.textend)--- but we could use one of the three notification methods accepted:--- sendNotification, sendNotificationSync or sendNotificationAsync--- by using the setText method:local notif = luce.NotificationType.sendNotificationlabel:setText( "another content", notif )--- set label editablelabel:setEditable(false, true, true) -- edit on single click, edit on double click,-- cancel changes when losing focus--- we can attach the label to the button toolabel:attachToComponent( button, true ) -- component, true: onLeft/false: 上面 (default)print( "is attached on left?", label:isAttachedOnLeft())--- set a colour for background and align text to the rightlocal label2 = luce:Label("Another Label")
    label2.text="(left aligned)"label2:setColour( label2.ColourIds.backgroundColourId, luce.Colours.yellow )
    label2:setJustificationType( luce.JustificationType.right )--- centre textlocal label3 = luce:Label("(left aligned text)")
    label3.text="(centered)"label3:setColour( label3.ColourIds.backgroundColourId, )
    label3:setJustificationType( luce.JustificationType.centred )------ create a TextEditor---local te = luce:TextEditor("Text Editor")--- directly set bounds for this componentte.bounds= { 200, 250, 200, 200 } -- x, y, w, h-- or-- te.setBounds{ 200, 250, 200, 200 }--- add our Document Window and components to our main JUCE applicationmainWindow:initialise(function(...)
     mc:addAndMakeVisible( button ) -- add the button to the main component button:setBounds{ 200, 20, 200, 200 } -- give the button some dimensions mc:addAndMakeVisible( label ) -- add the label--label:setBounds{ 20, 250, 100, 100 } -- don't set bounds to the label-- if you want it attached to button mc:addAndMakeVisible(te) -- add the Text Editor mc:addAndMakeVisible( button2 ) -- add the second button with the different lnf button2:setBounds{ 410, 20, 200, 200 }
     label2:setBounds{ 410, 230, 150, 30 }
     label3:setBounds{ 410, 270, 150, 30 }
     --mc:setBounds{ 0, 0, 800, 600 } -- set the component bounds-- as this is the last component before-- DocumentWindow, it'll set the window size-- too, unless dw sets one dw:setContentOwned( mc, true )
     dw:centreWithSize{800, 600} -- centre window on screen with size 800x600--dw:setCentrePosition{ 0, 0 } -- move it to the top left corner--dw:setBounds{ 100, 100, 800,600 } -- sets the window bounds-- as dw is a TopWindow and, as such, the very 1st component,-- it'll be positionned-- on screen directly, so that's another way of-- doing centreWithSize/setCentrePosition--dw:setSize{ 800,600 } -- just show the window, top left corner dw:setVisible(true)
     return dw -- return the Document Window so the JUCE Application can take itend)--- callback on DocumentWindow :closeButtonPresseddw:closeButtonPressed(function(...)
     print("*** DocumentWindow close button pressed")end)--- callback used when quit is askedlocal stop_now =falsemainWindow:systemRequestedQuit(function(...)
     print("** MainWindow system requested quit")
     stop_now =true mainWindow:quit()end)--- main loop--- there are two implementations of the main loop--- one is the JUCE's native wrapped one--- and the other one gives control over the loop--- so actions can be taken during the process execution-- luce:start( mainWindow ) -- the simplest one, everything's under-- JUCE control--- and the non automatic one--- the function's executed in a loop within a thread,--- so there's no need to loop here--- it is set with the same rate than the JUCE's loop (1ms by default)luce:start_manual( mainWindow, 1, function(...)
     -- do some stuff, like zmq:poll(), for instancereturn stop_nowend )



    项目 lTox讲座也很好的起点。



    • lua 5.1/lua 5.2/luajit 2.x

    编译 MODULE 和 C++ 项目

    • C++11
    • GCC 4.6 +/clang 3.3 +
    centos centos
    ~$ yum install -y git make gcc gcc-c++ libcurl-devel libX11-devel freetype-devel libXrandr-devel libXinerama-devel libXcursor-devel mesa-libGL-devel lua-devel
    ubuntu -16.10
    ~$ apt-get install -y git gcc g++ make libcurl4-gnutls-dev libx11-dev libfreetype6-dev libxrandr-dev libxinerama-dev libxcursor-dev mesa-common-dev libgl1-mesa-dev


    ~ $ apt-get install -y liblua5.1-0-dev lua5.1
    ~ $ apt-get install -y liblua5.2-0-dev lua5.2
    ~ $ apt-get install -y liblua5.3-0-dev lua5.3
    ~ $ apt-get install -y libluajit-5.1-dev luajit


    如果你信任,可以在 vbernat ppa知识库的haproxy-1.6 中找到 Lua 5.3.1包:

    ~$ sudo add-apt-repository ppa:vbernat/haproxy-1.6


    • JUCE 4.0.1 +


    从v0.3.1开始,模块可以同时适用于 lua5.1/luajit2.x 和 lua5.2.,在linux下支持 lua5.3。

    要使用lua脚本作为 MODULE,请选择 MODULE 下载。

    如果要贡献和帮助调试,请获取 MODULE的调试版本。

    如果你想使用 luce/嵌入式插件或者使用开发,请获取 static library ( 或者使用 MODULE 作为共享库,如果你愿意)。

    对于 iOS,只有 static 库不允许共享版本,但如果你想部署到,设备,我将提供它。

    对于 Android,只有共享库/MODULE 是可以用的,因为 static 库并不真正有意义,但是请随意询问。

    v0.3.4 ( alpha )

    Linux64 5.1 Linux64 5.2 Linux64 5.3 WIN32 5.1 WIN32-5.2 OS X 5.1 OS X 5.2iOS iOSiOS iOSAndroid 4.4-5.1 ( luajit )


    基本上,大部分核心组件都是实现的,以及 OpenGL。


    你可能发现的一些组件可能会丢失,因此请不要犹豫添加( 或者在 fork 中添加一个新的类,然后将新的类添加到 Luce,然后发送一个请求请求。)。


    所有Luce类都以 L 开头。

    Luce设计非常接近 juce,但通常是 simplfied,在lua中保持某些行为或者特性时,它们不会有意义。

    有关通用GUI设计的信息,请参见 JUCE。

    所有部件都派生自juce类的组件和luce类的LComponent。 所有非LBase类都是从派生的。 LComponent本身是由LBase派生的。 LBase提供了必需的链接到 Lua,而LComponent提供了Juce所需的链接。

    在Juce中存在的大多数回调也存在于Luce中;同样的方式需要在Juce中重写;在中,它们需要在Luce中重写,以便回调函数能够有效地进行回调。 如果未提供回调,则调用默认的Juce操作,如果有。

    所有L*类都映射它的Juce等效项或者特定于 Luce ( 像 LBase )。

    所有L*类在Lua代码中是可以重写的,像任何纯Lua模块,它们大部分在Lua中部分实现。 这对于回调声明或者向本机方法添加操作或者简单地为新的functionnalities专门化组件非常有用。 这是我们用来直接在Lua中实现 C++ 类的机制。



    为了简单和引用,Luce 通常使用与 JUCE 相同的方法名。 然而,在 JUCE 使用 getter/setter时,Luce提供了一个直接值属性,只要可能和明显,这就是。 例如 setName("...")getName() 将被 name [="..."] 替换,尽管 set/获取方法仍然可以访问。

    另一种不同的是对两种方法的重载;无论什么时候存在更多的表单,Luce都将始终实现这个 compact 表单,留下了详细的信息。

    比如,如果一个方法可以接受一个 rectangle 和四个数字,y,w,w,Luce将接受一个LRectangle或者表 {x,y,w,h},但不接受四个数字。


    Luce 并不直接提供任何侦听器类,而是封装侦听器,但在 C++ 方面包装侦听器;这样就可以将侦听器打包;尽管它们只是激活了一个侦听器,但是它们只是激活或者禁用了侦听的侦听器。


    另一个区别是 rectangle 和点对象,我们没有发现任何用于本机提供的。 这些类作为纯lua索引表提供,在需要的地方重新创建。 因此,JUCE 方法需要 Rectangle 或者 Point 对象时,必须提供包含值的表。 顺序始终为x,y [, w, h ]。 一般而言,它遵循类构造函数中声明的顺序。 稍后可能会有这些类的lua实现,提供一些最有用的方法,比如 :reduce()


    Luce 中有两种类: 完整的类和类类类。

    完整类只是扩展为与 Luce 集成的JUCE 类,而包装类则是在 Luce 组件之间创建某种 inheritence。

    对于 1st,请参见 LLabelLTextEditor 或者 LTextButton,而 2nd 类的唯一示例是 LComponent

    在继承 LComponent的同时,LLabelLTextEditorLTextButton 扩展各自的JUCE 组件,而继承了基类方法的所有 JUCEComponent

    这允许在 Luce 组件中模拟 inheritence,因为它在一个简单的方法中是不可能的。 如前所述,我们并不是针对 JUCE的1到 1绑定,因这里更复杂的机制在这里是大的( 除非有人知道一种简单的方法来实现这个)。

    有一个 helper 脚本 microparser,它大大简化了为 Luce 创建新类的工作。 它不会生成新的"准备使用"类,而是通过生成类模板和头部以及所有可用方法和回调来提供很大的帮助,并预先实现它们。 最明显的是完全生成的。

    若要添加伪 inheritence,请使用脚本

    ./ <Luce_CLASS_BASE_NAME>

    生成要包含在类中的LCLASS_inh.h 头。

    创建类之后,将它的包含在 luce.cppluce.h 中,并在 Main.cpp 中引用它:

    intl_NewClass(lua_State *L) {
    }staticconst luaL_reg luce_lib [] = {
     { "NewClass", l_NewClass },
     {NULL, NULL}

    Luce 路线图

    现在 Luce 已经稳定且完整,可以实现一些GUI应用程序。


    • 文档
    • 高级 API ( 例如创建类,例如 inheritence ( @see 30log )。
    • 集成调试设备
    • 事件广播,是juce还是其他内容
    • 可移植性:最终 Windows 和 OSX,Android和iOS的优化,启动 *BSD
    • 简化look-and-feel定制



    Luce Squishable,将Luce类的lua连接到一个可移植的文件。


    对于开源项目,使用的是 GPLv3的术语,其中部分部分是 AGPLv3。




    BASE  模块  gui  LUA  JUCE