Categories

Tags

Home

Kwik5 consists of the following two tools,

  1. Kwik exporter for Adobe Photoshop UXP plugin

    it publishes images(1x, 2x, 4x) and lua files of layer properties (x, y, width, height) to a selected App/book folder of a Solar2D project.

  2. kwik Visual Code editor for Solar2D

    the publsiehd project from Kwik exporter has an editing capabiliy on Solar2D simulator

    it attaches animation, button etc to a layer, and updates the project codes(lua files) when you save each properteis of kwik components/events(actions).

    circled A, B images are helper objects of Kwik to specify a Layer Animation position from pos A to pos B. You can drag A to starting position, B to ending podistion of a target layer.

    Lively preview is available when you enter a new value to component properties.

    Solar2D simulator automatically reloads itself when there are updated files. (Kwik4 needed publsihing code to update a solar2d project)

    the kwik editor can read single or mutiple books project.

You can create a single book app and mutiple books project with the following project structure.

Please set a App/book folder as the output folde when you publsih images/codes with Kwik exporter UXP plugin.


Generative AI

Generative AI

Creating assets

https://deepai.org/api-docs

Adobe

PS generative fill

Express Add On ⭐️

https://firefly.adobe.com/

Pr

Podcast


Audio

Misc

Text to audio

Free Sound Effects

Adobe Audition

How to organize

Whisper


Video

RunwayML Gen2 ⭐️


Stable Diffusion

Automatic1111

DreamStudio

ControlNet

https://github.com/Mikubill/sd-webui-controlnet

Cloud Providers

Machine Specs


Audio

how to get an audio file


Image

how to get an image

local url = "https://th.bing.com/th/id/OIG.1AwVedDzsmOL2HECayrn?pid=ImgGn"

function M:create(UI)
  local layerProps = self.layerProps or _layerProps
  --
  self.imagePath = layerProps.name.."." .. (layerProps.type or ".png")
  local path = UI.props.imgDir..self.imagePath
    -- path = system.pathForFile(UI.props.imgDir..self.imagePath, system.ResourceDirectory)
    -- print(url)
  local function networkListener( event )
    if ( event.isError ) then
        print ( "Network error - download failed" )
    else
        local object = event.target
        --object.alpha = 0
        transition.to( object, { xScale = 0.2, yScale=0.2, alpha = 1.0, onComplete = function()
          local function onColorSample( event )
            -- print( "Sampling pixel at position (" .. event.x .. "," .. event.y .. ")" )
            -- print( "R = " .. event.r )
            -- print( "G = " .. event.g )
            -- print( "B = " .. event.b )
            -- print( "A = " .. event.a )
            object.fill.effect = "filter.chromaKey"
            object.fill.effect.sensitivity = 0.1
            object.fill.effect.smoothing = 0.1
            object.fill.effect.color = { event.r,event.g,event.b, event.a }
          end
        local posX, posY = object.x - object.contentWidth/2+4, object.y-object.contentHeight/2+4
        display.colorSample( posX, posY , onColorSample )
        end } )
    end
    -- print ( "event.response.fullPath: ", event.response.fullPath )
    -- print ( "event.response.filename: ", event.response.filename )
    -- print ( "event.response.baseDirectory: ", event.response.baseDirectory )
  end
  --
  local obj = display.loadRemoteImage(url, "GET", networkListener, "testOne.jpg", system.TemporaryDirectory, display.contentCenterX, display.contentCenterY )
end

Particles

how to get particles


Spritesheet

how to get a spritesheet


Video

how to get a video file


all

Bookstore

Bookstore

TODO

Bookstore app contains the multiple kwik projects(books) inside. The lua files of each book are embedded in the app binary but the assets such as images, audio files of each book are not embedded in the app binary. They are downloadable from a web server. Due to Apple’s regulation, Solar2D does not allow to load the lua files (programming code) via Internet. The lua files must be embedded in the app binary but the other assets can be transfered from http server to the app.

A library page controls which book to be loaded with In App Purchase. User clicks a book icon on thubnail view on a library page, and a purchase dialog appears if user wants to buy one. A project called bookTOC(Table Of Content) contains library.psd and dialog.psd

When you like to add new books, you need to update the model.lua of bookTOC to include a new book information.

You can create as many as books but Bookstore is not designed to hold hundreds of books.


Sample file

Please download the sample project from here.

  Photoshop
   ├── bookFree
   │   ├── page1.psd
   │   └── page2.psd
   ├── bookOne
   │   ├── page1.psd
   │   └── page2.psd
   └── bookTOC
       ├── dialog.psd
       └── library.psd

  Solar2D
    ├── App
    │   ├── bookFree
    │   ├── bookOne
    │   └── bookTOC
    │
    ├── components
    |    ├── bookstore
    |        └── model.lua
    └── main.lua

  BookServer
    ├── compress_assets
    |       └── main.lua
    └── bookstore
        ├── bookFree
        └── bookOne

BooKServer

Solar2D


Solar2D

Solar2D project has main.lua in the root directory.

    ├─App
    │  ├─book01
    │  ├─book02
    │  └─bookTOC
    ├─extlib
    ├─lib

    build.settings
    config.lua
    main.lua

bookTOC

library.psd shows the thumbnail of books and the buttons for purchase and download

dilaog.psd shows a book image and the IAP buttons and information text


bookFree


bookOne

page1.psd


BookServer

Please use the script to zip the image, audio files as zip files


Components

Components

bookstore consists of the following files and modules

Photoshop files

library.psd

dialog.psd

table.psd


Solar2D components

bookstore component is defined in components.pages table. This calls bookstore.lua


model

Please fill the bookstore model with values of each book


view

You would like to customize the looks

marker

updateText

local M = {}
--
function M.new (host)
    local obj = {}
    local spinner
    obj.host = host
    --
    function obj:show(host)
        if not spinner then
            spinner = display.newGroup()
            local hostName = host or obj.host or ""
            --Place a progress spinner on screen and tell the user the app is contating the store
            local spinnerBackground = display.newRect(0,0,360,600)
            spinnerBackground:setFillColor(1,1,1,0.75)
            --Spinner consumes all taps so the user cannot tap the purchase button twice
            spinnerBackground:addEventListener("tap", function() return true end)
            local spinnerText = display.newText("Contacting " .. hostName .. "...", 0, -20, native.systemFont, 18)
            spinnerText:setFillColor(0,0,0)
            --Add a little spinning rectangle
            local spinnerRect = display.newRect(0, 0,35,35)
            spinnerRect:setFillColor(0, 0)
            spinnerRect:setStrokeColor(1,1,1)
            spinnerRect.strokeWidth = 2
            transition.to(spinnerRect, { time=4000, rotation=360, iterations=999999, transition=easing.inOutQuad})
            --Create a group and add all these objects to it
            spinner:insert(spinnerBackground)
            spinner:insert(spinnerText)
            spinner:insert(spinnerRect)
            spinner.x, spinner.y = _W/2, _H/2
            spinner.spinnerText = spinnerText
        end
    end

controller

These functions are available for page&book navigation. You can set it for an event action.


Book Server

Book Server

Preparation

After published your Book’s projects, you can use compress_assets/main.lua

compress_assets/main.lua

local command = require("compress_assets")

command.setServerFolder("macos", "bookstore")
--command.setServerFolder("win32", "bookstore")

-- the name of Kwik project and the name of In App Purchase product
--
local books = {
    {project = "Book01", serverFolder = "book01"},
    {project = "Book02", serverFolder = "book02"},
}

-- Use Online Images needs an image file
--
local onLineImages = {
    {project = "Book01", serverFolder = "book01", image = "assets/images/page1/bg@4x.png"},
    {project = "Book02", serverFolder = "book02", image = "assets/images/page1/bg@4x.png"},
}

Notice: For bookstoe with multiple languaes, and Use Online Image is true, a thumbnail image from online server will be requested with lang ID. For instance,


http-server

Please prepare a http-server and put the contents of BookServer folder.

http-server

cd BookServer
http-server

  http://127.0.0.1:8080
  Hit CTRL-C to stop the server

Update an asset

compress_assets/main.lua has command.updateAsset function

--
-- update page1, videos
--
local project      = "Book02"
local serverFolder = "book02"
local page         = 1
local type         = "images"

--   command.updateAsset(project, serverFolder, page, type)

--[[
    "audios"
    "read2me"
    "PNGs"
    "sprites"
    "particles"
    "WWW"
    "thumbnails"
    "images"
    "shared"
]]

Assets folder


Simulator

Simulator & Device Build

Run with Simulator

Build for device

After everything all right with simulator, turn off debug mode IAP.lua and set the valid product IDs of apple, google or amazon in the following lua files of BookShelfTOC project. You may edit the files in App/TOC.

components/store/IAP.lua

function M:init(catalogue, restoreAlert, purchaseAlert)
    print("iap init")
    self.catalogue       = catalogue
    self.restoreAlert  = restoreAlert
    self.purchaseAlert = purchaseAlert

    local iapOptions = {
        catalogue         = catalogue,
        filename          = "episodes_inventory.txt",
        --Salt for the hashing algorithm
        salt              = "something tr1cky to gue55!",
        failedListener    = failedListener,
        cancelledListener = failedListener,
        --Once the product has been purchased, it will remain in the inventory.  Uncomment the following line
        --to test the purchase functions again in future.  It's also useful for testing restore purchases.
        --doNotLoadInventory=true,
        debugMode        = true,
    }
    iap.init(iapOptions)
    print("iap init end")
end

components/store/model.lua

M.catalogue = {
    products = {
            Episode02 = {
            productNames = { apple="Episode02_apple", google="Episode02_googgle", amazon="Episode02_amazon"},
            productType  = "non-consumable",
            onPurchase   = function() IAP.setInventoryValue("unlock_Episode02") end,
            onRefund     = function() IAP.removeFromInventory("unlock_Episode02") end,
        },
            Episode03 = {
            productNames = { apple="Episode03_apple", google="Episode03_googgle", amazon="Episode03_amazon"},
            productType  = "non-consumable",
            onPurchase   = function() IAP.setInventoryValue("unlock_Episode03") end,
            onRefund     = function() IAP.removeFromInventory("unlock_Episode03") end,
        },
    },
    inventoryItems = {
            unlock_Episode02 = { productType="non-consumable" },
            unlock_Episode03 = { productType="non-consumable" },
    }
}

Steps

Steps

Project folders


Solar2D simulator

menu > show sand box folder

you need to clean the following folders if you want to recover the initial state. * Application Support * Documents * TemporaryFiles

For device build, you must set the valid product IDs from apple, google or amazon. For IAP Debug Mode, set the same name value to each filed.

Debug mode

IAP Badger https://github.com/happymongoose/iap_badger

When you test it with debug as true for components/bookstore/model.lua , you need to set the book names as its dummies for the productNames.

Don’t use the official product names from apple, google or amazon. With official IDs, debug mode fails to return a book name and IAP not work correctly.

  productNames = {apple = "bookFree", google = "bookFree", amazon = "bookFree"},

build.settings

iOS

if you use http server instead of https, please set your domain in build.settings

NSExceptionDomains

https://docs.coronalabs.com/guide/hardware/appleATS/index.html

Android


Book Versions

Book Versions

It is a feature to download a different language version of a book. A user pays for a book and choose to download a book with his/her language

Modified sample project is here.


BookSever

BookSever/compress_assets/main.lua

command.setServerFolder("macos", "bookshelf")
--command.setServerFolder("win32", "bookshelf")

local books = {
    {project = "Book01en", serverFolder = "book01en"},
    {project = "Book01jp", serverFolder = "book01jp"},
    {project = "Book02en", serverFolder = "book02en"},
    {project = "Book02jp", serverFolder = "book02jp"},
}

local onLineImages = {
    {project = "Book01en", serverFolder = "book01en", image = "build4/assets/images/p1/bg@4x.png"},
    {project = "Book01jp", serverFolder = "book01jp", image = "build4/assets/images/p1/bg@4x.png"},
    {project = "Book02en", serverFolder = "book02en", image = "build4/assets/images/p1/bg@4x.png"},
    {project = "Book02jp", serverFolder = "book02jp", image = "build4/assets/images/p1/bg@4x.png"},
}

Solar2D/components/bookstore/model.lua

the versions are added

M.books = {
  bookFree = {
    name         = "bookFree",
    versions     = {"en", "jp"},
    titles       = {en="Book Free", jp="ブック フリー"},
    descriptions = {en="free", jp ="無料"},
    isFree       = true,
    isOnlineImg  = false,
    image        = "App/bookFree/assets/images/title/bg.png",
    productNames = {apple = "bookFree", google = "bookFree", amazon = "bookFree"},
  },
  bookOne = {
    name         = "bookOne",
    versions     = {"en", "jp"},
    titles       = {en="book", jp="ブック"},
    descriptions = {en="$10",jp="1000円"},
    isFree       = false,
    isOnlineImg  = true,
    image        = "App/bookOne/assets/images/title/bg.png",
    productNames = {apple = "bookOne_apple", google = "bookOne_google", amazon = "bookOne_amazon"},
  }
}

when user purchase template, user can download the default of template or en, jp. Kwik internally downloads template.zip or bookXen/asset zip files or bookXjp/asset zip files

use case memo

TODO direct open a book according to the language code of the page


DAO

DAO

  NFT OpenSea
    graphics assets from books
      |
    Interactive Picture/Video Book
      IAP per book download

    AI generated video/images/stories
    Genre
    Artist/Author
    Way of Life

    to earn Art like DJ/VJ
    kwik app
      graphics -- nft market
        Lulu, possible trees, Will Terry etc
      free to play
      free to edit a page component and Share & Earn!
      playing piano to configure animation parameters
  draw to earn needs GPU brushes
  HX80G/StableDiffusion/ControlNet
  コミティア

Design

Design

Project model

Tools

Sample projects


Image Exporter plugin for Adobe Photoshop


Project Model

sample-projects/Pegasus

test/base-proj/Solar2D/App/bookFree


Visual Editor(React)

{{webview html panel}}


Visual Live Editor(Solar2D)

Solar2D/tools/kwik-editor/App/kwikEditor to be copied into a project? Or make them as components like a AtoB?

audio/group/timer/var are independent from layers


TODO


Tools

generate_scene_index


REST API

Tools

Exporrter plugin for PS, XD

├─Solar2D
│  ├─robotlegs
│  │  ├─App
│  │  │  ├─book
│  │  │  │  ├─assets
│  │  │  │  │  ├─images
│  │  │  │  │  │  └─page01
│  │  │  │  ├─commands
│  │  │  │  │  └─page01
│  │  │  │  │      └─layers
│  │  │  │  ├─components
│  │  │  │  │  └─page01
│  │  │  │  ├──models
│  │  │  │      └─page01
│  ├─template
│  │  ├─App
│  │  │  └─contentX
│  │  │      ├─assets
│  │  │      ├─commands
│  │  │      │  └─pageX
│  │  │      ├─components
│  │  │      │  ├─pageX
│  │  │      │  │  ├─audios
│  │  │      │  │  ├─layers
│  │  │      │  │  │   ├─animations
│  │  │      │  │  │   ├─images
│  │  │      │  │  │   ├─interactions
│  │  │      │  │  │   ├─physics
│  │  │      │  │  │   └─replacements
│  │  │      │  │  │       ├─particles
│  │  │      │  │  │       ├─sprites
│  │  │      │  │  │       ├─syncAudioText
│  │  │      │  │  │       ├─videos
│  │  │      │  │  │       └─www
│  │  │      │  │  ├─groups
│  │  │      │  │  ├─page
│  │  │      │  │  │  ├─controls
│  │  │      │  │  ├─timers
│  │  │      │  │  └─variables
│  │  │      │  └─store ?
│  │  │      ├─models
│  │  │      │  └─pageX
│  │  │      │      ├─commands
│  │  │      │      └─components
│  └─tools
│      ├─kwik-editor
└─UXP
    └─kwik-exporter
        ├─plugin
        │  ├─icons
        │  └─kwik
        │      ├─templates
        │      │  ├─components
        │      │  │  └─kwik
        │      │  ├─model
        │      │  │  ├─components
        │      │  │  ├─events
        │      │  │  └─layers
        │      │  └─scenes

Editor

REST Server

Utilities


REST Server

Rest Server

See get_started/custom_class as well


GET

returns .json of layer components(classes) or events/commands. It also returns default values of compoent properties

@host=http://localhost:9090

### run pegasus server if not running, and return books
GET /app

###
GET /bookFree

###
GET /bookFree/page1

### selectLayer
GET /bookFree/page1/title

###
GET /bookFree/page1/title/linear

POST

receives Props of layers and Commands and then renders .lua components/commands. It also stores the request params in .json

### modify layer props
POST /bookFree/page1/title/?command=preview
Content-Type: application/yaml

{
  alpha=0.5
}

### save layer props with the current value
POST /bookFree/page1/title/?command=save
Content-Type: application/yaml

PUT

creates a new lua file in App directory if not exist. scafolding a lua file

GET /app

###
GET /newBook
PUT /newBook
PUT /newBook/newPage

### update index.lua too
PUT /newBook/newPage/newLayer
PUT /newBook/newPage/newLayer/?class=linear

### custom class
GET /components/custom
PUT /components/custom/newClass
PUT /newBook/newPage/newLayer/?class=newClass

### events
GET /newBook/commands/newPage
PUT /newBook/commands/newPage/newEvent

DELETE

removes a lua file

###
DELETE /newBook/newPage/newLayer
DELETE  /newBook/newPage/newLayer/?class=linear

### custom class?
DELETE /components/custom/newClass
DELETE /newBook/newPage/newLayer/?class=newClass

### events
DELETE /newBook/commands/newPage/newEvent

Utilities

Utilities


Architecure

Architecure

UXP panel

flowchart LR

Designer((fas:fa-user Designer))
Developer((fas:fa-user Developer))
User((fas:fa-user User or AI))


subgraph Photoshop[Photoshop UXP]
	graphics(images/lua renderer<br>scaffolder)
end

subgraph Editor[Kwik Visual Editor]
	subgraph App
		assets[(assets/images<br>models/json)]
		lua[(Source .lua)]
	end
	tools(GUI tools<br><br>renderer<br>scaffolder)
	httpServer
	RestApi(RestApi<br>transform<br>animation)
	RestApi -.- tools
end

subgraph RestApi[REST API]
	form(Properties <br> CRUD)
end

subgraph VSCode
	httpYac(httpYac)
  coding
end

Photoshop -.img/json.-> assets
RestApi <-.img/json.-> httpServer
httpServer <-.-> assets
httpServer -.- tools
tools -.- App

User -.- Browser
Browser-.maybe in future.- RestApi
httpYac -.- RestApi
coding -.- lua

RestApi -.- assets

Designer --- tools
Designer --- Photoshop

Developer --- VSCode

Rest api

pegasus is runningin Editor

  1. run Editor

  2. upload an image to pegasus server

    • save it assets/images/book/
    • create display.object
    • .json

Kwik Visual Editor

  1. outputs images/json to App/book

  2. run App/book

Data store

A Kwik Project

flowchart RL


subgraph Editor[kwik editor]

  subgraph nanostores
    layerstore[layers]
    actions
    assets
    audios
  end

  GUI[ menu > Layer]

  subgraph asset
    assetSelectBox
    classProps
    buttons
  end

  subgraph parts
    selectors
    bookTable
    pageTable
    layerTable
    propsTable
    buttons
    subgraph controller
      selectLayer

    end
    controller -.- selectors
  end

  selectors -. 1 click .- GUI

  subgraph action
    actionTable
    actionCommandTable
    actionPropsTable
  end

  subgraph classEditors
    subgraph animation
      selectBox
      classProps
      controller
    end
    audio
  end
  controller
  template
  scripts

end



subgraph framework
  controller
  subgraph components_kwik[components]
    bookstore
    common[Common <br> myClass]
    custom
    layer
    anim
    button
  end
  command_kwik[command]
  plugin
  extlib
end


subgraph App/bookX
  assets
  subgraph components
      page(pageX.index <br> - layers <br> - audios <br> - ........)
  end

  subgraph commands
    pageX
  end
  scene(index.lua returns scenes as pageXs)
end

Select Layer to load the layer table

flowchart RL


subgraph Editor[kwik editor]

  GUI[ menu > Layer]

  subgraph asset
    assetSelectBox
    classProps
    buttons
  end

  action
  classEditors

  subgraph parts
    selectors
    bookTable
    pageTable
    layerTable
    propsTable
    buttons
    subgraph controller
      selectLayer

    end
    controller -.- selectors
  end

  selectors -. 1 click .- GUI

  controller
  template
  scripts

    subgraph nanostores
    layerstore[layers data]
    actions
    assets
    audios
  end

end

subgraph framework
end


subgraph App/bookX
  assets
  subgraph components
      page(pageX.index <br> - layers <br> - audios <br> - ........)
  end

  subgraph commands
    pageX
  end
  scene(index.lua returns scenes as pageXs)
end

selectLayer -. 2 read .-> components
selectLayer -. 3 set .-> layerstore

layerTable -. 4 listening for  createTable .- layerstore

Kwik Editor

Kwik Visual Editor


Kwik functions


Samples


Structure



Kwik Exporter Plugin for Photoshop

Kwik Exporter Plugin for Photoshop

Kwik4 created a project folder where psd files are placed. Kwik5 you can creat a folder for your psd files on your own. When Kwik Exporter opens, it asks for the folder location.

Steps to publsh images of layers of . psd files

  1. Manually create a project folder with Finder(mac) or Explorer (win), and put your psd files there.

  2. In Photoshop, extension > Kwik

    1. Open button for selecting your folder of .psd files

    2. Click to open one psd file

      • In Layers panel, you can set a color-code to a layer folder to tell Kwik for publishing each image of chidren.
    3. New or Select Book button

      please select a Solar2D project folder

    4. Publish button

      specify which .psd files to be processed

      • checkbox all the psd files, or input index numbers of each .psd in the list. ex “3-5”, or “1, 3, 5”

    For Active Document, you can choose

    • Export Code
    • Export Images

UI: Exporter plugins for PS, XD, Figma


Publish all images of selected documents

  1. mark checkboxes of document’s names you want to publish

    https://developer.adobe.com/xd/uxp/uxp/reference-spectrum/User%20Interface/

    spectrum tableview is not yet supported in UXP https://react-spectrum.adobe.com/react-spectrum/TableView.html

    • Ctl(Win) or Option(Mac) + Space key toggles selection for the focused row
    • toggle all on/off
  2. clcik

    opendFileDialog asks a book folder under App folder.


Active Document exports Images for an active document document

  1. open .psd by cliking the psd name in the list

    Kwik4_1280x1920.psd with background image 1440x2776

    kwik5 does not request the canvas size of .psd as 1280x1920 when publising

  2. clcik Export Images

    openFileDialog asks a book folder under App folder.


Layer Groups - Logic in Kwik

It controls exporting option of a layerSet(layer group) by creating a sub folder in a Solar2D project. This is done by chekcing the color code of a layer in Layers panel. If a color code for unmerge is set, it wiil export the chidlren of a layer set.

  1. create a folder with same name as layerSet in book/assets/images/FILE_NAME_OF_PSD

    for instance, “bg” is a layer group of kwik4_1280x1920.psd

    you can manually create the bg folder under App/book/assets/images/kwik4_1280x1920 so the export images function knows where to put images of sub layers of a group. So you can have each imagge of the member of a layer group.

    If such no folder with the same name as a layer group, the image of a layer group is exported.

> A concept of Kwik5 is to use App folder as a project base. It is a kind of file-based database where .json, .lua and assets files are placed. No more .kwk xml file of Kwik4 is there.

> Direclty Editing  a file under App folder while running Solar2D Simulator means a live editing.  There is no build4 folder of Kwik4 either

TODO Active Document > Layer Selection Only


Editor Frontend for PS, XD, Figma


Kwik Project Model

Project Model

test/base-proj/

TODO add a layer group

.
├── AndroidResources
├── App
│   ├── bookFree
│       ├── assets
│       │   ├── images
│       │   │   ├── page1
│       │   │   │   ├── bg.png
│       │   │   │   ├── bg@2x.png
│       │   │   │   ├── bg@4x.png
│       │   │   │   ├── gotoBtn.png
│       │   │   │   ├── gotoBtn@2x.png
│       │   │   │   ├── gotoBtn@4x.png
│       │   │   │   ├── title.png
│       │   │   │   ├── title@2x.png
│       │   │   │   └── title@4x.png
│       │   │   ├── page2
│       │   └── thumbnails
│       ├── commands
│       │   ├── page1
│       │   │   ├── eventOne.lua
│       │   │   └── eventTwo.lua
│       │   └── page2
│       ├── components
│       │   ├── page1
│       │   │   ├── audios
│       |   |   ├── layers
│       |   |   │   ├── bg.lua
│       |   |   │   ├── gotoBtn.lua
│       |   |   │   ├── gotoBtn_animation.lua
│       |   |   │   ├── index.lua
│       |   |   │   ├── title.lua
│       |   |   │   └── title_animation.lua
│       │   │   ├── groups
│       │   │   ├── page
│       │   │   ├── times
│       │   │   └── variables
│       |   ├── page2
│       │   └── index.lua
│       ├── models
│           ├── page1
│           │   ├── audios
│           │   ├── layers
│           │   ├── groups
│           │   ├── page
│           │   ├── times
│           │   ├── variables
│           │   ├── bg.json
│           │   ├── commands
│           │   │   └── eventOne.json
│           │   ├── gotoBtn.json
│           │   ├── gotoBtn_animation.json
│           │   ├── index.json
│           │   ├── title.json
│           │   └── title_animation.json
│           └── page2
│  
├── Images.xcassets
├── LaunchScreen.storyboardc
├── assets
├── build.settings
├── commands
│   ├── app
│   ├── common
│   │   └── myEvent.lua
│   └── kwik
├── components
│   ├── bookstore
│   ├── common
│   │   ├── bookstoreNavigation.lua
│   │   ├── index.lua
│   │   ├── keyboardNavigation.lua
│   │   ├── myComponent.lua
│   │   └── thumbnailNavigation.lua
│   ├── editor
│   └── kwik
├── config.lua
├── controller
├── en.lproj
├── extlib
├── lib
├── mySplashScreen.png
└── main.lua

Assets

Assets

TBI

https://www.kwiksher.com/doc/kwik_tutorial/interactions/drag_kwikcatpuzzle/


model

  UI.editor.assets = {
    audios = {
      {
        name = "click.mp3",
        path = "audios/short",
        links = {{page = "page1"}}
      }
    },
    videos = {
      {
      name = "videoA.mp4",
      path = "videos",
      links = {{page= "page01", layers = {"layerOne"}}}
      },
      {
        name = "videoB.mp4",
        path = "videos",
        links = {{page= "page01", layers = {"layerTwo"}}}
        }
    },
    sprites = {}

  }

UI

TBI

Create a new layer with a selected media

  1. select filename

    preview video?

  2. select the icon on the top of asset table swith component view.

    Please select a layer for replacement or hold + key to add a new layer

    TODO if assetTable is open, don’t show layer props Table

    save button will add a layer as same as the filename w/o extension

    renaming a layer needs to update layerName_class.lua, models and the entry in layers table in index.lua. Use layer properties for renaming

  3. edit cotnrol props

    TODO hide the icons of assset table

    TODO update video props

  4. save button

    TODO


REST API

editor asset selectors.tree new, modiy, delete (hide filter for selectbox) show (doGet) > assetsTable

/book/assets
/book/assets/audios
/book/assets/audios/short
/book/assets/audios/long
/book/assets/audios/sync
/book/assets/images
/book/assets/sprites
/book/assets/videos

editor > assets


BinaryArchive

https://github.com/siudesu/BinaryArchive

Bookstore tool

Use BookServer/compress_assets/main.lua


Audio

Audio asset

long

short

sync

A

wordTouch: clicking a word in a line, one mp3 for one word is played

file structure (physical)

TODO:rename sentenceDir to wordTouch?

── sync
├── alphabet
│   ├── a.mp3
│   ├── b.mp3
│   └── c.mp3
├── alphabet.mp3
├── alphabet.txt

lua table (logic)

audioProps = {
  language    = nil,
  folder = nil,
  filename = "alphabet.mp3",
  sentenceDir = "alphabet",
}

textProps = {
  filename = "alphabet.txt",
}

alphabet.txt

0.000 0.500  A
0.500 1.000  B
1.000 1.500  C

B

en and jp

── sync
  ├── en
  │   ├── i_am_a_cat
  │   │   ├── am.mp3
  │   │   ├── cat.mp3
  │   │   └── i.mp3
  │   ├── i_am_a_cat.mp3
  │   ├── i_am_a_cat.txt
  └── jp
      ├── i_am_a_cat
      │   ├── am.mp3
      │   ├── cat.mp3
      │   └── i.mp3
      ├── i_am_a_cat.txt
      ├── i_am_a_cat.mp3
audioProps = {
  folder    = nil
  language  = {"en", jp"},
  filename = "i_am_a_cat.mp3",
  sentenceDir = "i_am_a_cat",
}

textProps = {
  filename = "i_am_a_cat.txt",
}

C


Asset and Component Releation

Asset and Component Releation

graph RL


subgraph editor
  tool[video replacement tool]
  tool -.- default([defautprops])
  readAssetFunc[readAsset]
end

subgraph App.bookX
  subgraph assets
    short_ballsCollision.mp3
    video.mp4
    json[(assets.json for editor)]
  end

  subgraph components.pageX
    audio_short_ballsCollision.lua
    subgraph layers
      bg.lua
      imageOne.lua -.- layerProps([props table])
      imageOne_video.lua -.- videoProps([props table])
    end
  end
end

readAssetFunc -. 1 traverse with lfs .-> App.bookX
readAssetFunc -. 2 generate .-> json
YourInput -. 3 selecting layer <br> and video .-> tool
tool -. 4-1 read/write .- json
json -. 4-2 link for publish .- video.mp4
tool -. 5 render when publshing .-> imageOne_video.lua

subgraph photoshop
  kwik([kwik plugin])
end
generativeAI[[Generative AI]]

kwik -. images .-> assets
kwik -. lua files .-> layers

generativeAI -. images audios videos .-> assets

Four ways to compose layers with assets.

  1. plugin in photoshop > editor > layer replacement
  2. vs code to compose index.lua manually
  3. vs code to compose assets.json
  4. httYac to compose index.lua

plugin in photoshop > editor > layer replacement

  1. Kwik plugin in Photoshop to publish images and lua files
  2. put media files into App.bookX/assets folder
  3. use Kwik Visual Editor
    1. select a layer

    2. select a layer replacement tool (ex video tool)

    3. save the props of video replacement

      the selected layer is linked to the selected media

    4. pubish

//TODO screenshots

//TODO readAsset to merge/validate assets.json with /components/pageX/index.lua?

editor logic


vs code to compose index.lua manually

  1. put media files into App.bookX/assets folder

assets.json will be generated automatically by editor

  1. vs code to compose App/booxX/components/pageX/index.lua

    add a layer element to components.layers table with a class (ex “video”)

    local scene = require('controller.scene').new(sceneName, {
        name = "page1",
        components = {
          layers = {
                {  bg={
                                  } },
                {  layerOne={ class={"video"} } },
          },
          audios = {},
          groups = {},
          timers = {},
          variables = {},
          page = { }
        },
        commands = { "eventOne", "eventTwo", "act01" },
        onInit = function(scene) print("onInit") end
    })
    
  2. App/booX/comonents/pageX/layers

    add the lua files with the props

    • layerOne.lua
      local layerProps = {
        blendMode = "normal",
        height    =  1280 - 0,
        width     = 1920 - 0 ,
        kind      = pixel,
        name      = "bg",
        type      = "png",
        x         = 1920 + (0 -1920)/2,
        y         = 0 + (1280 - 0)/2,
        alpha     = 100/100,
        }
      
    • layerOne_video.lua
      local props = {
        actionName    = "{{actionName}}",
        delay         = {{delay}},
        iterations    = {{iterations}},
        name          = "{{name}}",
      }
      return require("components.kwik.layer_video").new(props)
      

editor logic

when Solar2D simulator refreshes automatically, and then editor read pageX/index.lua for updating assets.json

readAsset() uses lfs to read media files in App.bookX/assets folder, and generate layer names of links table for assets.json out of pageX/index.lua


vs code to compose assets.json

you can scafold .lua files with the asset command/bat. The asset_lua_generator(.command/.bat) generates lua files into the following directories with assets.json/yml/lua

  1. put media files into App.bookX/assets folder

  2. prepare assets.json (yml, lua are supported) to spefify layers with media files

  3. scafold with the asset_lua_generator in the vs code terminal

  4. edit each lua manually for positions and props

    you may use common/align.lua for auto layout

    or use editor/httpYac to modify the props of layers and components

    // TODO Move/Transform tool in editor

    // TODO asset_lua_generator

note

if you don’t use kwik plugin in Photoshop, scafolding with assets.json will help you work wtih media files

if layes are not found in page/index.lua, sset_lua_generator will add them to index.lua

you can use asset_lua_generator after publshing images/lua files from Kwik plugin in Photoshop, and then manually associateing layers and media files for replacements

if you run asset_lua_generator for the second time, it will not overwirte existing lua files. It will scafold a new lua file for a new component which is not found in pageX/index.lua


httpYac to compose index.lua

  1. put media files into App.bookX/assets folder

  2. put (create) a layer and layer_xxx lua files with httpYac

    ## PUT
    
    ### creates a new lua file in App directory if not exist. scafolding a lua file
    
    @host=http://localhost:9090
    
    GET /app
    ###
    GET /book
    PUT /book
    GET /book/page01
    
    ### update index.lua too
    PUT /book/page01/newLayer
    PUT /book/page01/newLayer/?class=video
    
    POST/book/page01/newLayer
    Content-Type: application/yaml
    {
      alpha=0.5
      x=display.contentCenterX
      y=display.contentCenterY
      width=100
      height=100
    }
    
  3. post(update) a layer with class

    
    GET /book/assets
    
    POST /book/page/newLayer/?class=video
    
    url: video.mp4
    
    POST /book/page/newLayer/?class=video
    
    url:  https://kwiksher.com/tutorials/Video/kwikplant.mp4
    

//TODO update assets.json


memo

      selectors.tree new, modiy, delete (hide filter for selectbox)
      show (doGet)  > assetsTable
        /book/assets
        /book/assets/audios
        /book/assets/audios/short
        /book/assets/audios/long
        /book/assets/audios/sync
        /book/assets/images
        /book/assets/sprites
        /book/assets/videos


        lua table (asset.json) will be saved into asset folder

        file info(fixed props) // should we reference props table in App/bookX/components/pageX?
                               // yes, they are tangible props of a component in a page
          - audio_props.lua
          ```
            audioProps = {
              language    = nil,
              folder = nil,
              filename = "alphabet.mp3",
              sentenceDir = "alphabet",
            }

            textProps = {
              filename = "alphabet.txt",
            }
          ```

         - spritesheet_props.lua
          ```
          {
              filename = "butflysprite.png",
              sheetInfo = {
                width = 188,
                height = 188,
                sheetWidth = 376,
                sheetHeight = 188,
              }
          }
          ```

         - video_props.lua
         ```
         ```


      doPut
        assets audio, video, spritesheet, synctext
        fetch it from url
          openapi plugin whisper with timecode? ⭐️
        dummy assets from sample and rename to use it

      doPost for an asset to preview?
        this needs a position to tell where to insert it in layers'tree so use

          ```
          POST /book/page/newLayer/?class=video

          file: video.mp4

          ```

          save it to App/book/models/assets.json

          lfs to traverse files in asset folder  except images. This table is also updated from replacement tools
          for instance, user selects a video replacement and choose a videoA.mp4 to layerOne on page1,

            - page1/layerA_video.jspn

            - assets.json
              ```
              {
                audios = {},
                fonts = {},
                -- images = {}
                particles = {},
                sprites = {},
                videos = {
                  {
                    name = "videoA.mp4",
                    path = "videos",
                    links = {{page= "page01", layers = {"imageOne"}},
                            {page= "page02", layers = {"iamgeTwo"}}}
                  },
                },
              }
              ```

          editor > assets
            - select

              videoA.mp4 layers = page1/layerOne,  page2/layerTwo

              option only current page

            - add

                a new layer is added to index.lua with the asset class with deafult props on a current page

            - modify

               edit and save properties

            - delete
              remove layers, option to delete from a disk

    doPost
      for transition2.to, it needs a generic form

      PUT to set a class for a layer
        - add it to index.lua, model.json
        - scafold layer_class.lua with a template

        it entry exists, and no params, get values from controller (controlProps) which has been updated by POST?

      POST to add properties
        GUI

      if it is not one of kwik's classes, first create a new custom class

        PUT components/custom/newClass

        this class.lua does not have a template, props is a lua table that can be encoded into json

Particles

Particles asset


Spritesheet

Spritsheet asset

TODO rename it as Imagesheet?


Adobe Animate

https://kwiksher.com/blog/2019/08/09/sprite-sheet-from-adobe-animate/


Video

Video asset

Local

App/bookX/assets/video folder

── video
  ├── videoA.mp4
  ├── animals
        ├─ cat.mp4
        ├─ dog.mp4

lua table

{
    isLocal = true,
    url = "videoA.mp4",
}
{
    isLocal = true,
    url = "animals/dog.mp4",
}

Remote

https://kwiksher.com/tutorials/Video/kwikplant.mp4

{
    isLocal = false,
    url = "https://kwiksher.com/tutorials/Video/kwikplant.mp4",
}

Web

Web html asset


Commands

Commands

editor.action.model

M.commands = {
  action = {
    play = {_trigger = ""},
    playAll = {actions = {}, random = true},

  },
  animation = {
    pause = {_target = ""},
    resume = {_target = ""},
    play = {_target = ""},
    playAll = { animations = {}}
  },
  audio = {
    record = {
      duration = 0,
      mmFile = "",
      malfa = "",
      audiotype = ""
    },
    muteUnmute = {
      _target = {}
    },
    play = {
      _target = "",
      type = "",
      channel = "",
      repeatable = "",
      delay = "",
      loop = "",
      fade = "",
      volume = "",
      tm = "", -- timer id
      _trigger = "",
    },
    rewind = {
      _target = "",
      type = "",
      channel = "",
      repeatable = "",
    },
...

template/commands/pageX/actionX.lua

local ActionCommand = {}
local AC           = require("commands.kwik.actionCommand")
---
function ActionCommand:new()
  local command = {}
  --
  function command:execute(params)
    local UI         = params.UI
    local sceneGroup = UI.scene.view
    local layers      = UI.layers
    local obj        = params.obj
{{#actions}}
  {{#animation}}
    --
    -- target layer :sceneGroup[layerName]
    -- target animation : layer.animations[index]
    --
    {{#pause}}
      AC.Animation:pause("{{target}}")
    {{/pause}}
    {{#resume}}
      AC.Animation:resume("{{target}}")
    {{/resume}}
    {{#play}}
      AC.Animation:play("{{target}}", {{index}})
    {{/play}}
  {{/animation}}
  {{#button}}
    {{#onOff}}
    AC.Button:onOff("{{target}}", {{enable}}, {{toggle}} ) -- enable, toggle
    {{/onOff}}
  {{/button}}
{{/actions}}
  end
  return setmetatable( command, {__index=AC})
end

commands.kwik.actionCommand

animationAction.lua

local M = {}
--
function _M:pause(anim)
end
--
function _M:resume(anim)
end
--
function _M:play(anim)
end
--
return _M

canvas

Canvas Action Command

action editor > Interactions > Canvas

//defined in editor.action.model.lua

M.commands{
  ...
  canvas = {
    brush = {
      size = {size = 10, alpha = 1},
      color = {0,0,0,1}
    },
    erase = {},
    undo  = {},
    redo = {}
  },
  ...
  ```

action editor

Action editor model

index.lua

.
├── actionCommand
│   ├── cancel.lua
│   ├── delete.lua
│   └── save.lua
├── actionCommandButtons.lua
├── actionCommandPropsTable.lua
├── actionCommandTable.lua
├── actionTable.lua
├── buttons.lua
├── commandbox.lua
├── controller
│   ├── cancel.lua
│   ├── copy.lua
│   ├── create.lua
│   ├── delete.lua
│   ├── index.lua
│   ├── paste.lua
│   ├── save.lua
│   ├── selectAction.lua
│   └── selectActionCommand.lua
├── index.lua
├── model.lua
└── selector.lua
local M = {name = name, views = {
  "index", -- this creates actionIcon button
  "selector", -- context:mapCommands "selectAction", "selectActionCommand"
  "actionTable", -- lists actions in a page
  "actionCommandTable",
  "actionCommandPropsTable",
  "commandbox", -- animation.play, pause, ..
  "buttons",
  "actionCommandButtons"
}}

...
...

function M:create(UI)
  if self.sceneGroup then return end
  --
  self.sceneGroup = UI.editor.sceneGroup
  controller:init(UI, self.sceneGroup, categoryMap, selectbox)
  -- print("create", self.name)
  local posX, posY = display.contentCenterX/2 + 42, -2
  -----------------------------------------
  self:createIcon(UI, posX, posY)
  -----------------------------------------
  self:createSelectbox(UI, posX, posY)
  -----------------------------------------
  local scrollListener  = function(e) end
  local scrollView = widget.newScrollView{
       top                    = 22,
       left                   = display.contentCenterX,
       width                  = 100,
       height                 = 240,
    -- height                 = #models*18,
       scrollWidth            = display.contentWidth*0.5,
       scrollHeight           = display.contentHeight*0.8,
       hideBackground         = false,
       isBounceEnabled        = false,
       verticalScrollDisabled = false,
       backgroundColor        = {1.0},
       listener               = scrollListener
  }
  self.sceneGroup:insert(scrollView)
  -----------------------------------------
  self:createTable(scrollView, models)
  -----------------------------------------
  UI.editor.actionEditor = self
  self.sceneGroup.actionEditor = scrollView
  --
  self:hide()
  -- self:show()
end

screenshot

Screenshot Action Command

action editor > Interactions > screenshot

//defined in editor.action.model.lua

M.commands{
  ...
  screenshot = {
    take = {
      ptit = "",
      message = "",
      shutter = true,
      hideLayers = {}
    }
  },
  ```

UI

UI

action is selected

one actionCommand is selected


Components

Components

example bookFree/page1

.
├── assets
├── commands
├── components <==== page components
│   ├── page1
│   │   ├── audios
│   │   ├── layers
│   │   │   ├── bg.lua
│   │   │   ├── gotoBtn.lua
│   │   │   ├── gotoBtn_animation.lua <=== animation component lua
│   │   │   ├── title.lua
│   │   │   └── title_animation.lua <=== animation component lua
│   │   ├── groups
│   │   ├── page
│   │   ├── times
│   │   ├── variables
│   │   └── index.lua
│   └── page2
├── models
│   ├── page1
│   │   ├── bg.json
│   │   ├── events
│   │   │   └── eventOne.json
│   │   ├── gotoBtn.json
│   │   ├── gotoBtn_animation.json <=== animation component json
│   │   ├── index.json
│   │   ├── title.json
│   │   └── title_animation.json <=== animation component json
│   └── page2

Animation

Animation

UI

index template

template

module

editor

defaults


Audio

Audio

UI

index template

template

module

components.editor.audio

audio
├── audioTable.lua
├── buttons.lua
├── controlProps.lua
├── controller
│   ├── cancel.lua
│   └── save.lua
├── defaults
│   └── audio.lua
├── index.lua

Button

Button


Canvas

Canvas

PSD

Unit Test

Kwik4

index

genereated from editor.template.componetns/pageX/index.lua

{
    name = "pageX",
    components = {
      layers = {
        {  back={ } },
        {  painting={} },
        {  butBlue={ class={"button"}} },
        {  butWhite={class={"button"}} },
        {  butOrange={class={"button"}} },
        {  butCamera={class={"button"}} },
        {  butLarge={class={"button"}} },
        {  butMedium={class={"button"}} },
        {  bigCandice={class={"button"}} },
        { Candice={class = {"canvas"}},
      },
    },
    commands = {},
}

template

module

editor

// editor.ihdex:getTool() returns a component editor from id of editor.model

action commands

Please see design/project_model/commands/canvas and screenshot


Group

Group

TODO page4’s groups = {“SubA”, “GroupA”, “myGroup” }


Layer

Layer

page1

page4 for demonstrating layers and groups

local sceneName = ...
--
local scene = require('controller.scene').new(sceneName, {
    name = "page4",
    components = {
      layers = {
            {  bg={} },
            {  copyright={} },
            {  star={} },
            {  GroupA={
                  { Ellipse = {} },
                  { SubA = {
                      { Triangle = {} },
                    }
                  },
               }
            },
            {  hello={} },
      },
      audios = {},
      groups = {"SubA", "GroupA", "myGroup" },
      timers = {},
      variables = {},
      page = {}
     },
    commands = {
        -- "myAction",   "myEvents.testHandler",
      },
    onInit = function(scene) print("onInit") end
})
--
return scene

Particles

Particles

create main.lua file

display.setStatusBar( display.HiddenStatusBar )
local particleDesigner = require( "particleDesigner" )
local kaboom = particleDesigner.newEmitter( "'+jname+'_temp.json" )
kaboom.x = display.contentWidth / 2
kaboom.y = display.contentHeight / 2

Model

{
  configName = '',
  textureFileName = '',
  common = {
    duration = 0,
    sourcePositionVariancex = 0,
    sourcePositionVariancey = 0,
    maxParticles = 0,
  },
  emitter = {
    emittyerType = 0, -- 0=grevity, 1=radial
    angle = 0,
    angleVariance = 0,
    gravity = {
      speed = 0,
      speedVariance = 0,
      gravityx = 0,
      gravityy = 0,
      radialAcceleration = 0,
      radialAccelVariance = 0,
      tangentialAcceleration = 0,
      tangentialAccelVariance = 0,
    },
    radial = {
        maxRadius = 0,
        maxRadiusVariance = 0,
        minRadius = 0,
        minRadiusVariance = 0,
        rotatePerSecond = 0,
        rotatePerSecondVariance = 0,
    }
  },
  particles = {
    particleLifespan = 0,
    particleLifespanVariance = 0,
    startParticleSize = 0,
    startParticleSizeVariance = 0,
    finishParticleSize = 0,
    finishParticleSizeVariance = 0,
    rotationStart = 0,
    rotationStartVariance = 0,
    rotationEnd = 0,
    rotationEndVariance = 0,
  },
  colors = {
    startColorAlpha =   0,
    startColorRed =   0,
    startColorGreen =   0,
    startColorBlue =  0,
    startColorVarianceRed = 0,
    startColorVarianceGreen = 0,
    startColorVarianceBlue = 0,
    startColorVarianceAlpha = 0,
    finishColorAlpha = 0,
    finishColorRed = 0,
    finishColorGreen = 0,
    finishColorBlue = 0,
    blendFuncSource = 0,
    finishColorVarianceRed = 0,
    finishColorVarianceGreen = 0,
    finishColorVarianceBlue = 0,
    finishColorVarianceAlpha = 0,
    blendFuncDestination = 0,
  },
}
var st = String(myParticles.blendFuncSource)
switch (st) {
    case '0':
        st=0;
        break;
    case '1':
        st=1;
        break;
    case '774':
        st=2;
        break;
    case '775':
        st=3;
        break;
     case '770':
        st=4;
        break;
    case '771':
        st=5;
        break;
    case '772':
        st=6;
        break;
    case '773':
        st=7;
        break;
    case '776':
        st=8;
        break;
 }
//
var st2 = String(myParticles.blendFuncDestination)
switch (st2) {
    case '0':
        st2=0;
        break;
    case '1':
        st2=1;
        break;
    case '774':
        st2=2;
        break;
    case '775':
        st2=3;
        break;
     case '770':
        st2=4;
        break;
    case '771':
        st2=5;
        break;
    case '772':
        st2=6;
        break;
    case '773':
        st2=7;
        break;
    case '776':
        st2=8;
        break;
 }

//Default

//image/texture
if (oriParticleFile != true && preview == true) {
    myParticles.textureFileName = '"'+newParticleFile+'"';
}  else if (oriParticleFile != true && preview != true) {
    ico = dlg.partName.e.text+".png";
    ico = ico.replace(/"/gi, "")
    myParticles.textureFileName = '"'+ico+'"';
  }  else if (oriParticleFile == true && preview != true) {
    ico = dlg.partName.e.text+".png";
    ico = ico.replace(/"/gi, "")
    myParticles.textureFileName = '"'+ico+'"';
} else {
    ico = ico.replace(/"/gi, "")
    myParticles.textureFileName = '"'+ico+'"';
}

//particles content

//Color content
switch (st) {
    case 'Zero':
        st=0;
        break;
    case 'One':
        st=1;
        break;
    case 'Dst_Color':
        st=774;
        break;
    case 'One_Minus_Dst_Color':
        st=775;
        break;
      case 'Src_Alpha':
        st=770;
        break;
    case 'One_Minus_Src_Alpha':
        st=771;
        break;
    case 'Dst_Alpha':
        st=772;
        break;
    case 'One_Minus_Dst_Alpha':
        st=773;
        break;
    case 'Source_Alpha_Saturate':
        st=776;
        break;
  }
  myParticles.blendFuncSource = st;

  switch (st1) {
    case 'Zero':
        st1=0;
        break;
    case 'One':
        st1=1;
        break;
    case 'Dst_Color':
        st1=774;
        break;
    case 'One_Minus_Dst_Color':
        st1=775;
        break;
      case 'Src_Alpha':
        st1=770;
        break;
    case 'One_Minus_Src_Alpha':
        st1=771;
        break;
    case 'Dst_Alpha':
        st1=772;
        break;
    case 'One_Minus_Dst_Alpha':
        st1=773;
        break;
    case 'Source_Alpha_Saturate':
        st1=776;
        break;
  }
  myParticles.blendFuncDestination = st1;

function jsonXML(file) {
        if (garb == "{") {
            toxml += garb.replace("{", "<particles>");
        } else if (garb == "}") {
            toxml += garb.replace("}", "</particles>");
        } else if (garb.search("configName") > 0 ) {
            toxml += '<configName>"'+configName+'"</configName>';
        } else if (garb.search("textureFileName") > 0 ) {
            toxml += '<textureFileName>"'+imagefile+'"</textureFileName>';
        } else {
            garb = garb.replace('"', "<")
            var para = garb.substring(1,garb.search('":'))
            garb = garb.replace('":', ">");
            garb = garb.replace(',', "");
            garb = garb+"</"+para+">";
            toxml += garb;
        }
    }

function xmJSON(file) {
    var j = "{ \r\n"
    for (var xn=0;xn<=myParticles.elements().length()-1;xn++) {
        if (xn!=myParticles.elements().length()-1) {
            j += '   "'+myParticles.child(xn).name()+'" : '+myParticles.child(xn)+', \r\n';
        } else {
            j += '   "'+myParticles.child(xn).name()+'" : '+myParticles.child(xn)+' \r\n';
        }
    }
    j += "}"
}

Spritesheet

Spritesheet

TODO

UI

index template

template

module

editor

defaults


Sync Audio&Text

TODO

UI

default

local M = {
  name = "alphabet",
  class = "sync",
  controls = {
    autoPlay     = true,
    delay        = nil,
    fadeDuration = 1000,
    speakerIcon  = true,
    wordTouch    = true,
  },
  audioProps = {
    filename = "alphabet.mp3",
    channel = 2,
    volume      = 10,
  },
  textProps = {
    folder       = nil,
    font         = nil1,
    fontColor   = { 1,1,1 },
    fontColorHi = { 1, 1, 0 },
    fontSize    = 36,
    language    = nil,
    padding     = 10,
    readDir     = "leftToRight",
    sentenceDir = "alphabet", -- wordTouch
  }
}

M.line = {
  { start =  0, out = 1000, dur = 0, name = "A", file = "a.mp3", action = "onComplete"},
  { start =  1000, out = 2000, dur = 0, name = "B", file = "b.mp3", action = "onComplete"},
  { start =  2000, out = 3000, dur = 0, name = "C", file = "c.mp3", action = "onComplete"},
}

Sync Audio & Text

test/base-proj/Solar2D/App/bookFree/components/page3/index.lua

local sceneName = ...
--
local scene = require('controller.scene').new(sceneName, {
    name = "page3",
    components = {
      layers = {
            { test={}},
            { alphabet    = {class={"sync"}}},
            { iamacat     = {class={"sync"}}},
            { mynameiskwik = {class={"sync"}}},
      },
      audios = {
        long  = {"GentleRain", "Tranquility" },
        short ={"ballsCollision"} ,
      },
      groups = {},
      timers = {},
      variables = {},
      page = { }
    },
    commands = { "onComplete", "play" },
    onInit = function(scene) print("onInit") end
})
--
return scene

files(mp3 and text) are structeud in the following patterns A-D. See the examples of sync audio text in page3/index.lua


A: alphabet

local props = {
  name = "alphabet",
  filename = "alphabet.mp3",
  type = "sync",
  autoPlay = true,
  channel = 2,
  folder = nil
}

props.text = {
  { start =  0, out = 1000, dur = 0, name = "A", file = "a.mp3", action = "onComplete"},
  { start =  1000, out = 2000, dur = 0, name = "B", file = "b.mp3", action = "onComplete"},
  { start =  2000, out = 3000, dur = 0, name = "C", file = "c.mp3", action = "onComplete"},
}

props.wordTouch    = true
props.sentenceDir  = "alphabet" -- wordTouch
props.layer        = "alphabet"

B: i am cat

local props = {
  name      = "i_am_a_cat",
  filename = "i_am_a_cat.mp3",
  type      = "sync",
  language  = {},
  autoPlay  = true,
  channel   = 2,
  folder    = nil
}

props.language.en = {
  { start =  0, out = 500, dur = 0, name = "I", file = "i.mp3"},
  { start =  500, out = 1000, dur = 0, name = "am", file = "am.mp3"},
  -- { start =  1000, out = 1000, dur = 0, name = "a", file = nil},
  { start =  1000, out = 1500, dur = 0, name = "a cat.", file = "cat.mp3", action = "onComplete"},
}

props.language.jp ={
  {start =  0, out = 500, dur = 0, name = "ぼく", file = "i"},
  {start =  500, out = 1000, dur = 0, name = "ねこ", file = "cat", action = "onComplete"},
}

props.wordTouch    = true
props.sentenceDir  = "i_am_a_cat" -- wordTouch
props.layer        = "iamacat"

C: my name is kwik

local props = {
  name =  "my_name_is_kwik",
  filename = "my_name_is_kwik.mp3",
  type =  "sync",
  language = {},
  folder = "page3",
}
--
-- action is added by sync props
--
props.language.en = {
  { start =  0, out = 500, dur = 0, name = "My", file = "" },
  { start =  500, out = 1000, dur = 0, name = "name", file = ""},
  { start =  1000, out = 1000, dur = 0, name = "is", file = ""},
  { start =  1000, out = 1500, dur = 0, name = "Kwik.", file = "", action = "onComplete"},
}

props.language.jp = {
  {start =  0, out = 500, dur = 0, name = "なまえ", file = ""},
  {start =  500, out = 500, dur = 0, name = "は", file = ""},
  {start =  500, out = 1000, dur = 0, name = "くいっく", file = "", action = "onComplete"},
}
props.wordTouch    = false
props.sentenceDir  = nil -- wordTouch
props.readDir      = "leftToRight"
props.layer        = "mynameiskwik"

D: my father is nice

local props = {
  name =  "myfatherisnice",
  filename = "my_father_is_nice.mp3",
  type =  "sync",
  language = {},
  folder = nil,
}
--
-- action is added by sync props
--
props.language.en = {
  }

props.language.jp = {
}
props.wordTouch    = false
props.sentenceDir  = nil -- wordTouch
props.readDir      = "leftToRight"
props.layer        = "myfatherisnice"

Timer

Timer

UI

template

module

components.editor.timer

timer
  ├── buttons.lua
  ├── controller
  │   ├── cancel.lua
  │   └── save.lua
  ├── defaults
  │   └── timer.lua
  ├── index.lua
  └── timerTable.lua

Video

Video

UI

template

Video Replacement allows you to configure a video that will appear in replacement of an image layer. For example, you can draw a blank rectangle image in photoshop, and then you publish images to Solar2D project.In Kwik visiual editor, select the blank rectanglelayer you want to replace for a video and click Video replacement on Kwik editor.

Properties about the current layer content will appear when you click a layer in the list. It is important to match the same layer content size and position with the final video size.

module

components.replacement.index


Content X Structure

Content X Structure

test/base-proj/Solar2D/editor.template

obsolete develop/UXP/kwik-exporter/plugin/kwik/base-proj/Solar2D/editor.template

.
├── assets
│   ├── audios
│   │   ├── long
│   │   ├── short
│   │   └── sync
│   │       ├── en
│   │       ├── jp
│   ├── fonts
│   ├── images
│   │   └── pageX
│   ├── model.json
│   ├── particles
│   ├── sprites
│   ├── thumbnails
│   ├── videos
│   └── www
├── build.settings
├── commands
│   └── pageX
├── components
│   └── pageX
│       ├── audios
│       ├── layers
│       │    ├── layer_image.lua
│       ├── groups
│       ├── page
│       │   └── controllers
│       ├── timers
│       ├── variables
│       └──index.lua
├── config.lua
├── mediators
├── models

example bookFree/page1

.
├── assets
│   ├── images
│   │   ├── page1
│   │   │   ├── bg.png
│   │   │   ├── bg@2x.png
│   │   │   ├── bg@4x.png
│   │   │   ├── gotoBtn.png
│   │   │   ├── gotoBtn@2x.png
│   │   │   ├── gotoBtn@4x.png
│   │   │   ├── title.png
│   │   │   ├── title@2x.png
│   │   │   └── title@4x.png
│   │   └── page2
│   └── thumbnails
│       ├── page1.png
│       └── page2.png
├── commands
│   ├── page1
│   │   └── eventOne.lua
│   └── page2
├── components
│   ├── page1
│   │   ├── audios
│   │   ├── layers
│   │   │   ├── bg.lua
│   │   │   ├── gotoBtn.lua
│   │   │   ├── gotoBtn_animation.lua
│   │   │   ├── title.lua
│   │   │   └── title_animation.lua
│   │   ├── groups
│   │   ├── page
│   │   ├── times
│   │   ├── variables
│   │   └── index.lua
│   └── page2
├── main.lua
├── mediators
│   ├── page1Mediator.lua
│   └── page2Mediator.lua
├── models
    ├── page1
    │   ├── bg.json
    │   ├── events
    │   │   └── eventOne.json
    │   ├── gotoBtn.json
    │   ├── gotoBtn_animation.json
    │   ├── index.json
    │   ├── title.json
    │   └── title_animation.json
    └── page2

Icons Launch/Splash screens

https://forums.solar2d.com/t/new-icon-size-for-ios/354492

iOS loads Apple’s launch images of LaunchScreen.storyboardc made by Xcode. You can create it with Xcode.


Kwik4 https://kwiksher.com/doc/kwik_toolset/project_and_pages/project_properties/publish/

Copy these png files to ./build folders ?

iOS

Android

TBI for Adaptive Icons Android 8 and Later

HTML5

Linux

Windows

macOS

android(FireTV)

tvos(appleTV)

assets/tvAsset/tvosLaunch-assets/Icon-tvOS-Launch.png
assets/tvAsset/tvosLaunch-assets/Icon-tvOS-TopShelf.png

assets/tvAsset/tvosPallax-assets/Icon-tvOS-Large-Background.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Large-LogoA.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Large-LogoB.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Large-LogoC.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Large-LogoD.png

assets/tvAsset/tvosPallax-assets/Icon-tvOS-Small-Background.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Small-LogoA.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Small-LogoB.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Small-LogoC.png
assets/tvAsset/tvosPallax-assets/Icon-tvOS-Small-LogoD.png

Photoshop File

Photoshop File

https://www.ios-resolution.com/

iPhone 13 Pro Max 1284x2778

Kwik4 Ulitimate Config

note


Settings

Settings

https://docs.coronalabs.com/guide/distribution/buildSettings/index.html

https://docs.coronalabs.com/guide/distribution/advancedSettings/index.html

When orientation supports both of landscape and portrait, the main content area (a display group) should be translated to the center of screen.

each layers generated from Photoshop has fixed number of coordiate (x, y), for instance the center position of landscape 1920x1080 is at (1920/2, 1080/2) in landscape. If the actual device is rotated to portrait, the center position is (1080/2, 1920/2), for screen rotation event is detected, let’s move each layer by (1920/2 - 1080/2, 1080/2- 1920/2)

  sceneGroup.x, sceneGroup.y = display.contentCenterX, display.contentCenterY


Orientation Implementation

TODO this plugin locks the screen orientation but can not find the document.

   {
        ["plugin.orientation"] = {
            publisherId = "tech.scotth",
        },
    },

TODO save & cancel buttons goes out at the bottom in landscape


android

android


HTML5

HTML5

Getting started with HTML5

https://docs.coronalabs.com/guide/html5/plugins/index.html

https://www.solar2dplayground.com/


iOS

iOS


Linux

Linux

https://forums.solar2d.com/c/beta-testing/linux/118

https://snapcraft.io/solar2d

https://forums.solar2d.com/t/how-to-build-solar2d-on-linux/354787/3

audio.loadSound() won’t work https://forums.solar2d.com/t/audio-loadsound-wont-work/355589

Linux and Zerobrane Setup https://forums.solar2d.com/t/linux-and-zerobrane-setup/354500


macOS

macOS

Icon

these .ico and .icns files are created with the following commands in Termnal app. Plese make icon_xx.png files and install image magic to Window PC

rm -r icon.iconset
cp -r desktop_icon-assets icon.iconset
cd icon.iconset
rm icon_48x48.png
cp icon_32x32.png icon_16x16@2x.png
mv icon_64x64.png icon_32x32@2x.png
cp icon_256x256.png icon_128x128@2x.png
cp icon_512x512.png icon_256x256@2x.png
mv icon_1024x1024.png icon_512x512@2x.png
cd ..
iconutil --convert icns --output Icon-osx.icns icon.iconset

tvOS

tvOS

Icons

android(FireTV)

tvos(appleTV)

Parallax Previewer App

Solar2D does not need .lsr file to buld TVOS app. it needs those Logo.png

PSD files - Image assets from layers


Win32

Win32

Icon

set MYDIR=desktop_icon-assets
set ICON_FILE=Icon-win32.ico
magick ./%MYDIR%/icon_16x16.png ./%MYDIR%/icon_32x32.png ./%MYDIR%/icon_48x48.png %ICON_FILE%

Image Magick http://www.imagemagick.org/


Template Model

Code Template Model

assets/model/schema

embedded in codes – TBI to be extracted

TODO:add ext codes

extCodes
    libs
    p1
        user_codes.lua
        ext_001.lua
        ext_002.lua

        commands/
            button_name_001.lua
            action_name_001.lua
            user_codes.lua
    p2/

=> build4/

```lua
function ActionCommand:new()
    local command = {}
    --
    function command:execute(params)
        local UI         = params.UI
        local sceneGroup = UI.scene.view
        local layer      = UI.layer
        local phase     = params.event.phase
        local event     = params.event
        {{#vvar}}
            {{vvar}}
        {{/vvar}}
        {{#arqCode}}
            {{arqCode}}
        {{/arqCode}}
    end
    return command
end
```

or

ext_lib_codes.lua
```
local _K = require "Application"
{{#extLib}}
    local {{name}} = requireKwik("{{libname}}")
{{/extLib}}
--
{{#TV}}
local kInputDevices = require("extlib.tv.kInputDevices")
{{/TV}}

function _M:localVars(UI)
    local sceneGroup  = UI.scene.view
    local layer       = UI.layer
    {{#extCodeTop}}
    {{ccode}}
    {{arqCode}}
    {{/extCodeTop}}
end
```

model.json

{
    "page":1,"alias":"title","isTmplt":false,
    "audios":[null],
    "read2me":[null],
    "videos":[null],
    "PNGs":[null],
    "sprites":[null],
    "particles":[null],
    "WWW":[null],
    "thumbnails":[null],
    "images":[
                 "bg@4x.png", "bg@2x.png", "bg.png",
        null
    ],
    "shared":[
        null
    ]
}

{
    "page":{{page}},"alias":"{{alias}}","isTmplt":{{isTmplt}},
    {{#layers}}
        "{{layer}}":{
            "x":{{x}}, "y":{{y}},
            "width":{{width}}, "height":{{height}},
            "alpha":{{alpha}}, "ext":"{{ext}}" },
    {{/layers}}
    "audios":[
        {{#audios}}"{{filename}}",{{/audios}} null],
    "read2me":[
        {{#read2me}}{"foldername":"{{foldername}}", "filenames":[{{#filenames}}"{{.}}",{{/filenames}} null] },{{/read2me}} null],
    "videos":[
        {{#videos}}"{{filename}}",{{/videos}} null],
    "PNGs":[
        {{#PNGs}}"{{foldername}}",{{/PNGs}} null],
    "sprites":[
        {{#sprites}}"{{filename}}",{{/sprites}} null],
    "particles":[
        {{#particles}}{"filename":"{{filename}}","PNG":"{{PNG}}"},{{/particles}} null],
    "WWW":[
        {{#WWW}}{"filename":"{{filename}}","foldername":"{{foldername}}"},{{/WWW}} null],
    "thumbnails":[
        {{#thumbnails}}{{#filenames}}"{{.}}",{{/filenames}} {{/thumbnails}}null],
    "images":[
        {{#images}}
         "{{filename}}@4x.{{filetype}}", "{{filename}}@2x.{{filetype}}", "{{filename}}.{{filetype}}",
        {{/images}}
        null
    ],
    "shared":[
        {{#shared}}
         "{{filename}}@4x.{{filetype}}", "{{filename}}@2x.{{filetype}}", "{{filename}}.{{filetype}}",
        {{/shared}}
        null
    ]
}

Template strurcture

template structure

├── App
    ├── contentX
        ├── assets
        │   ├── audios
        │   │   ├── short
        │   │   ├── long
        │   │   └── sync
        │   ├── images
        │   └── model.json
        ├── commands
        │   └── pageX
        ├── components
        │   ├── pageX
        │   │   ├── audios
        │       │── layers
        │       │    ├── animations
        │       │    ├── images
        │       │    ├── interactions
        │       │    ├── physics
        │       │    └── replacements
        │       ├── groups
        │       ├── page
        │       ├── timers
        │       ├── variables
        │       └── index.lua
        │
        ├── defaults
        │   ├── audio_properties.xml
        │   ├── layer_properties.xml
        │   ├── linear_anim_properties.xml
        │   └── spritesheet_properties.xml
        ├── models
            ├── assets
            │   ├── audios
            │   │   ├── short
            │   │   ├── long
            │   │   └── sync
            │   ├── index.json
            │   ├── particles
            │   ├── sprites
            │   ├── videos
            │   └── www
            ├── lproj
            └── pageX
                ├── components
                │   ├── audios
                │   ├── layers
                │   ├── groups
                │   ├── page
                │   ├── timers
                │   └── variables
                ├── commands
                ├── index.json

Sample Projects

Sample Projects


Solar2D

Solar2D


Command line Build

Command line Build

https://github.com/joehinkle11/Automated-Corona-Builder

Windows

Mac

Node.js ( HTML5 Builder )


Future requests

New Features

Solar2D API to be included to Kwik framework

Solar2D video texture

Tiled

Texture atlas function in kwik?

Audio sprites?


Interactive

Lottie ts to lua? ⭐️


Workflow

Workflow

Overview

  1. put .png/.jpg into App/contentX/assets/images manually or use kwik-export plugin for Ps/XD to publish images to App folder

    • images without coordinates
    • images with coordinates from Ps, XD with kwik-export plugin
      • models/assets/images/*.json
  2. kwik-editor traverses the assets folder to output .json for models and .lua for scenes/pageX and components/pageX

    • kwik-generate-model
    • kwik-generate-index
    • kwik-scaffold-lua

    you can edit props or positions of images or attach a type of class such as animation, button …

    you can creat an event and a corresponding action of code such as playAnimation, hideLayer, playAudio …

    if you manually add a .lua file, you need to update scenes/pageX/index.lua too. You can use kwik-generate-index that traverses pageX folder of commands, components and scenes for synclonizing the lisf of .lua for pageX context defined in the index.lua.

1. assets

├── App
    ├── contentX
        ├── assets
            ├── audios
            │   ├── short
            │   ├── long
            │   └── sync
            ├── images
            │   └── pageX
            │       ├── bg.png
                    ├── folder
                    |      ├─  .png
                    |      └── .png
                    └── .png

1-1 images

Alternatively

1-2 audios

put audio files under assets/audios folder

.
├── assets
│   ├── audios
│   │   ├── short
│   │   │   └── ballsCollide.mp3
│   │   ├── long
│   │   │   ├── Gentle-Rain.mp3
│   │   │   └── Tranquility.mp3
│   │   └── sync
│   │       ├── en
│   │       │   ├── cat.mp3
│   │       │   ├── kwik.mp3
│   │       │   └── narration.mp3
│   │       ├── jp
│   │       │   ├── cat.mp3
│   │       │   ├── kwik.mp3
│   │       │   └── narration.mp3
│   │       ├── pageX_Text1.mp3
│   │       ├── pageX_Text1.txt
│   │       ├── page02_Text1.mp3
│   │       └── page02_Text1.txt

Lua Code

Lua

you don’t need to use kwik-generate-model nor kwik-editor to output lua files. You can skip making .json files of these tools, and you create a lua file manually into a folder, and append a name of additional file to scenes/pageX/index.lua

At runtime, Kwik Code Framework reads scenes/pageX/index.lua to load each .lua files of pageX. The object names for commands, compnents, scenes are defined in the index.lua.

kwik-genereate-index is a tool to update the index.lua from traversing the folders above.

  1. create .lua for commands, components, layers of pageX,
  2. run the tool to generate components/pageX/index.lua

you don’t need to use kwik-generate-index tool. You can manually edit it but it would be better to generate the index.lua with the tool.

Alternatively, there is another tool named kwik-scaffold-lua. This tool scafolds .lua files from components/pageX/index.lua. The tool does not overwrite .lua if exists, and may delete .lua if not defined in index.lua

Which Workflow do you like?

I like A because thinking about files/folders strcure with a file explorer, and coping/pasting an exsiting file could be easier when coding is in progress.

To initiate a project, B would work quicky to make a skelton structure.


REST API

REST API - YAML

  1. put images in App/demo/assets/page01

    • logo
    • panel
    • message
    • button
    • background
    • shape1
    • shape2

    Tool

    • Finder/File Expolorer
    • Plugin for Photoshop or XD will export images
    • Editor
      • html post image form
    • network
  2. Create layer structure

    • Editor lists the images
      • Order Top/Bottom/Up/Down

    frontEnd uses Adobe React Spectrum (in future, support screen reader etc)

    REST API to get the list of images and post the orderd list back

    GET /layers

      - background: []
      - button: []
      - logo: []
      - message: []
      - panel:
    

    Change the order of layers

    POST /layers
    
      - logo: []
      - panel:
          - message: []
          - button: []
      - background: []
    
  3. Set animation and button

    test KwikTheCat for evaluation

    one by one

    POST /layers/logo
    
    classes:
    - animation
    

    Or

    POST /layers/logo
    Content-Type: application/yaml
    
    - transition: bounce
    - params:
        height: 400
        width: 200
        time: 1000
        iterations: 0
    

    GUI in Edtior(Solar2D) for transformation, draggable to set a position

    POST /layers/panel/button
    
    classes:
    - button
    

    All together

    POST /layers
    
    - logo: []
      classes:
      - animation
    - panel:
      - message: []
      - button: []
        classes:
        - button
    - background: []
    

Get Started

TODO

Get Started

UXP/kwik-exporter

  1. Open Adobe UXP Developer Tool

    Add Plugin by selecting develop/UXP/kwik-exporter/dist/manifest.json

    Kwik Exporter Panel apppears

  2. From the UXP plugin

    Photoshop Files > Open

  3. Double Click the one of .psd in the list to open it for editing.

  4. Solar2D Project > Select Book or create a new Solar2D project

  5. Publish

    selected psd files are publihsed to App/book folder


A project folder

 ├── Photoshop
 │   └── book
 │       ├── kwikconfig.json
 │       ├── page1,psd
 │       └── page2.psd
 └── Solar2D
     ├── rel
     ├── src
     │   ├── App
     │   │   └── book <==== the published images/lua files are inside this folder
     │   ├── Images.xcassets
     │   ├── LaunchScreen.storyboardc
     │   ├── assets
     │   ├── build.settings
     │   ├── commands
     │   ├── components
     │   ├── config.lua
     │   ├── controller
     │   ├── en.lproj
     │   ├── extlib
     │   ├── jp.lproj
     │   ├── main.lua
     │   └── mySplashScreen.png
     ├── templates
     └── tools

Solar2D Simulator/kwik-editor

Open main.lua in Solar2D/src with Solar2D Simulator

Kwik Visual Code Editor wiil be loaded and your content(App/book) of your psd files are previewd

For instance, animtion editing

Action editing


Component & Asset

Compoent with Asset

audio

  1. App/book/assets/audios/long/GentleRain.mp3

  2. App/book/components/page3/index.lua

    local sceneName = ...
    --
    local scene = require('controller.scene').new(sceneName, {
        name = "page3",
        components = {
          layers = { },
          audios = {
            long  = {"GentleRain", "Tranquility" },
            short ={} ,
          },
          groups = {},
          timers = {},
          variables = {},
          page = { }
        },
        commands = { "onComplete", "play" },
        onInit = function(scene) print("onInit") end
    })
    --
    return scene
    
  3. App/book/components/page3/audios/long/GentelRain.lua

    local name = ...
    
    local props = {
      name     = "GentleRain",
      type     = "stream",
      autoPlay = false,
      channel  = 1,
      folder   = nil,
      filename = "GentleRain.mp3",
    }
    
    local M = {
      -- name        = {{aname}},
      -- type        = {{atype}},
      -- language    = nil  -- or {"en", "jp"},
      -- filename    = "{{fileName}}",
      -- folder      = nil
      -- allowRepeat = false,
      -- autoPlay    = {{aplay}},
      -- deplay      = {{adelay}},
      -- volume      = {{avol}},
      -- channel     = {{achannel}}
      -- loops       = {{aloop}},
      -- fadein      = {{tofade}},
      -- retain      = {{akeep}}
    }
    -- you can play it with UI.audios[self.name]:play()
    
    return require("components.kwik.page_audio").set(props)
    

layer replacement

TODO reame controls to asset?

For instance, a spritesheet consists of an imagesheet png in assets/sprites folder, properties like sequence data are defined in layerA_sprite.lua in components/page/layers folder, and components/page/index.lua declears the sprite class for layerA

  1. App/book/assets/sprites/imagesheet.png

    spritesheet.lua in the same folder has the sheetInfo properites.

  2. App/book/components/index.lua

    local scene = require('controller.scene').new(sceneName, {
        name = "page1",
        components = {
          layers = {
                {  bg={} },
                {  layerA={class={"sprite"}} },
          },
          audios = {},
          groups = {},
          timers = {},
          variables = {},
          page = {}
        },
        commands = {},
        onInit = function(scene) print("onInit") end
    })
    
  3. App/book/components/layers/layerA.lua

      -- layer properties
      local Props = {
        blendMode = "normal",
        height    = 100,
        width     = 100,
        kind      = pixel,
        name      = "layerA",
        x         = display.contentCenterX,
        y         = display.contentCenterY,
        alpha     = 100/100,
      }
    
  4. App/book/components/layers/layerA_sprite.lua

    sheetInfo “spritesheet” loads spritesheet.lua in the same folder

      local M = {
      name = "spritesheet",
      class = "spritesheet",
      type  = "uniform-sized", -- TexturePacker, Animate
      assets = {
        filename = "imagesheet.png",
        sheetInfo = "spritesheet",
        sheetContentWidth  = 376, -- same size or loaded from sheetInfo
        sheetContentHeight = 188, -- same size or loaded from sheetInfo
        numFrames          = 2,   -- same size or loaded from sheetInfo
        width              = 188, -- same size, disable for TP, Aniamte
        height             = 188, -- same size, disable for TP, Animate
      },
      sequenceData = {
        {
            name = "default",
            count = 2,
            loopCount = 0,
            loopDirection = "forward", -- reverse after last frame
            pause = false,
            start = 1,
            time = 1000,
        },
        {
          name = "test",
          frames = {1,2},
          loopCount = 0,
          loopDirection = "forward", -- reverse after last frame
          pause = false,
          time = 1000,
      }
      },
      actioneName = "",
    }
    

Components

Components

For instance components/pageX/page/swip.lua enables a page transition, and components/pageX/audios folder can have multiple audio lua files for instance short/audioOne.lua, long/audioTwo.lua. short audio file is loaded in to memory with audio.loadSound(), and long audio file is streaming with audio.loadStream().

local sceneName = ...
--
local scene = require('controller.scene').new(sceneName, {
    name = "pageX",
    components = {
      layers = {
          {  bg={} },
      },
      audios = { short = {"audioOne"}, long= {"audioTwo" },
      groups = {  },
      timers = {  },
      variables = {  },
      page = { "controllers.swipe" }
     },
    commands = { },
    onInit = function(scene) print("onInit") end
})
--
return scene

page

audio

group

timer


Common components

You can add a common component to components/common directory

And let it pass in arguments of bootstrap function in main.lua

local common = {commands = {"myEvent"}, components = {"myComponent"}}

require("controller.index").bootstrap({
  name="book", sceneIndex = 1, position = {x=0, y=0},
  common =common
})

Common components are executed after all the layer components of a scene are rendered. So you can access a layer component by UI.layers table.

for instance, myComponent.lua attaches a tap listener


function _M:didShow(UI)
  local sceneGroup = UI.scene.view

  UI.layers.bg:addEventListener("tap", function(event)
      print("bg is tapped")
  end)

end

Custom Class

Custom class

Put a lua file of your own class in commompinets/custom folder

Set your classname to a layer in App/bookX/pageX/index.lua

local sceneName = ...
--
local scene = require('controller.scene').new(sceneName, {
    name = "page1",
    components = {
      layers = {
            {  bg={ } },
            {  gotoBtn={ } },
            {  title={ class={"myClass"} } },
      },
      audios = {},
      groups = {},
      timers = {},
      variables = {},
      page = { }
    },
    commands = { },
    onInit = function(scene) print("onInit") end
})
--
return scene

myClass.lua will be called with a display image object of a layer associated with

  local yourLibrary = require("lib.yourLibrary")
  --
  --
  local M = {}

  --
  function M:init(UI)
  end
  --
  function M:create(UI)
    local obj = UI.sceneGroup[self.name]
  end
  --
  function M:didShow(UI)
  end
  --
  function M:didHide(UI)
  end
  --
  function M:destroy(UI)
  end

  M.set = function(instance)
    return setmetatable(instance, {__index=M})
  end
  --
  return M

properties can be defined in App/bookX/pageX/components/layers/layerX_myClass.lua

M.params={
  mytext = "hello my class",
}
...
...
return M

class as a name space

you can use a classname as name space

For instance, transition2.lua is a custom class and it has functions like “to”, “from”, “moveBy” etc.

you can speficy a function name as classoption with “.” in App/bookX/components/pageX/index.lua

 M.params ={
   {x = 260, delay = 400},
   {y = 260, xScale = -0.8, delay = 800},
   {xScale = -1, yScale = 1, delay = 400},
   {x = 60, xScale = -0.6, yScale = 0.6, delay = 1200},
   {rotation = 135, delay = 400},
   {x = 260, y = 60, alpha = 0, delay = 400},
   {rotation = 0, alpha = 1, delay = 800},
   {x = 60, delay = 400},
   {xScale = 0.8, yScale = 0.8, delay = 400}
 }

for imported class

use classOption with an opion value for instance “to” for trasition2 in M.props

local M={
  path = "bookTest.components.page2.layers.three.index",
  name = "three",
}
M.props = {
   five_transition2 = {classOption="to"},
}
return require("components.kwik.importer").new(M)

Generic Editor

ToDo

REST httpYac

### selectLayer with class if exists or not
GET /bookFree/page1/title/?class=transition2.to

###
POST /bookFree/page1/title/?class=transition2.to
Content-Type: application/lua

{
	{x = 260, delay = 400},
	{y = 260, xScale = -0.8, delay = 800},
	{xScale = -1, yScale = 1, delay = 400},
	{x = 60, xScale = -0.6, yScale = 0.6, delay = 1200},
	{rotation = 135, delay = 400},
	{x = 260, y = 60, alpha = 0, delay = 400},
	{rotation = 0, alpha = 1, delay = 800},
	{x = 60, delay = 400},
	{xScale = 0.8, yScale = 0.8, delay = 400}
}

Kwik Editor Screen Orientation

build.setting with landscape

build.setting with portrait

The editable tables for spritesheet, and sync text&audio looks lik this


Commands & Events

Commands & Events

For an event for a page in a book

commands/myAction.lua are triggeded with a dispatchEvent fucntion

  UI:dispatchEvent({
    name = "myAction",
    UI = UI
  })

this myAction is defined in commands table in components/pageX/index.lua

For a button, you see in components/pageX/index.lua, buttonOne layer has two events. they are tap and drag. These tap and drag events are handleld with commands/buttonOne/tap.lua and commands/buttonOne/drag.lua

commands are called as Action in Kwik. You can dispatchEvent with params to myAction.lua, myEvents.testHandler.lua

{
    name = "kwik4_1280x1920",
    layers = {
          {  bg={} },
          {  buttonOne={ events = {tap, drag}} },

    },
    components = {
      audios = { },
      groups = {  },
      timers = {  },
      variables = {  },
      others = { }
     },
    commands = { "myAction", "myEvents.testHandler" },
    onInit = function(scene) print("onInit") end
}

The assicated lua files are located in the commands folder


For a common action for a book

create a command lua in commands/common directory, for example

And let it pass in arguments of bootstrap function in main.lua

local common = {commands = {"myEvent"}, components = {myComponent={}}}

require("controller.index").bootstrap({
  name="book", sceneIndex = 1, position = {x=0, y=0},
  common =common
})

context:init function of controller/ApplicationContext.lua automtaically adds it

this context init is called everytime when a scene is loaded. You can dispatch an event to executre myEvent.lua like this

UI:dispatchEvent({
  name = "common.myEvent",
  UI = UI
})

Import component Layer

Import Layer Component

index.lua

layers = {
  ...
  rectCopied = {class = {"imported"}
  ...
  }

rectCopied_imported.lua

local props={
  path = "bookTest.components.parts.layers.buttonGroup.redRect",
  name = "rectCopied",
  class = {"button", "linear"},
  text = "hello importer class",
}

local layerProps = {
  x         = display.contentCenterX - 150,
  y         = display.contentCenterY - 100,
  color     = {1, 1, 0}
}

props.layerProps = layerProps
props.classProps = {
  button = {},
  linear = {}
}
return require("components.kwik.importer").new(props)
graph TB

subgraph parts
  bg
  square
  subgraph buttonGroup
    redRect
    greenRect
    blueRect
  end
  subgraph redGroup
   rectCopied
   circleRed
  end
  rectCopied -.import.->  redRect

end

subgraph page1
  bg1[bg]
  square1[square]
  buttonGroup1[buttonGroup]
end
square1 -.import.-> square
buttonGroup1 -.import.-> buttonGroup

TODO


Page1


Parts

components/layers files


Instance Name

the syntax is {{parent}}.{{name}}. You can access it like


PSD Naming Rule

Naming .psd file anad layers in documents

  1. Project and file names

    Avoid long names for your projects and files and, DO NOT use characters like +-<>%,#;!.

  2. Layer names

    Before you start adding buttons and animations, follow the rules below. It is much easier to add interactivity with finalized names rather than to edit all of them afterward. Basic rules include:

    • Only user Western characters are allowed for layer names.

      • DO NOT use characters such as + - <> %,#;!. for naming layers

        These characters conflict with Lua language. Kwik removes these “strange” characters but, if they were used before in any button actions, it would generate errors during export.

      • Do NOT name your layers starting with numbers.

        For example, a layer named “1”, will generate an error. A layer named “01_Name” will also generate an error. However, a layer named “Name_01” is correct.

      • Do not use Lua commands as names.

        For example, a layer named “if” will conflict with the command if. Some Lua commands are: if, end, local, transition, play. A full list of Lua commands is here. Kwik will generate an alert error if it finds layers named with Lua commands

    • Avoid having multiple layers with the same name.

      Kwik will not export the second layer with the same name as it will overwrite the first one, (which will make the Lua code crazy). Kwik will provide an alert error if it finds layers with identical names.

    • Avoid long names.

      All layer names become variables when exported, meaning they use more memory and are more difficult to read. Also, they are going to be shortening anyways, as Lua will not allow variable names with more than 15 characters.

      • Keep Layer Names Short for text layers

        Text layer names are originally created using the content of the text from the canvas in Photoshop. If you have a long paragraph, the layer name will be the entire content of the long paragraph! Long layer names cause problems in the generated code, so edit the layer names to make them shorter. (This is the most common problem we have seen from our users reporting issues.) Don’t forget all text, besides the ones used in Sync audio feature, also are exported as images.

  3. Grouped layers

    Kwik can export grouped layers as a single image. Use them to create more complex elements (for example, a multi-layered button with text, shadows, etc.), or for the creation of static elements. A common issue is to have all page elements in a group layer that images are not rendered separately. If you have a group with several layers, try to flatten them whenever it is possible. It will make the export process much faster.

  4. Button and Animation names of Kwik components

    Although Kwik offers an auto-naming feature, try to enter your own names (follow the rules above). It will help you to quickly edit your actions from the project view. This is a time saver for a project with several actions.


PSD Screen Size Orientation

config.lua

width = 320,
height = 480,
scale = "letterBox"

build.settings

Default Landscape

page4.psd


Default Portrait

page_portrait.psd


Solar2D Template

Project Template

you can check out App/book folder from github instead of using Kwik UXP plugin to publish a book folder.

// TODO github url

The checked out book folder has page1 folder without any *.lua nor image files. If you want to rename the page name, just use Finder or File Explorerer, and change the table value in index.lua


Installation

System requirments


Step 1 - UXP

UXP/kwik-exporter

  1. Open Adobe UXP Developer Tool

    Add Plugin

    Add Plugin to select develop/UXP/kwik-exporter/dist

    TODO alpla release folder structure may be different to the current develop

    Load com.kiwksher.kwik5.dev-ps

Kwik Exporter Panel

The UXP panel apppears in Photoshop

Photoshop Files > Open

you can select the following folder that comes with demo .psd files and kwikconfig.json. You may open a folder of yours which contains psd files.

TODO Project Name Text Box to display a selected folder name as default.

TODO User can change it and Kwik uses it for a folder name when publshing

TODO kwikconfig.json will be created in the selected folder.

TODO igonred

```
  "pages":[
    {"psd":"page01.psd", "name":"page one"},
    {"psd":"page02.psd", "name":"page two"}
  ],
  "ignored":[
    {"psd":"page01_copy.psd", "name":"page 01 backup"},
  ]
```

Double Click the one of .psd in the list. The psd file is opened for editing.

Solar2D Project

You may create a new project or select one that has been made.

This is a sample folder structure

  ├── Photoshop
  │   └── book
  │       ├── kwikconfig.json
  │       └── page01.psd
  └── Solar2D
        ├── App
        │   └── book
        │        ├──assets
        │        ├──commands
        │        ├──components
        │        ├──models
        │        └── index.lua
        ├── Images.xcassets
        ├── LaunchScreen.storyboardc
        ├── assets
        ├── build.settings
        ├── commands
        ├── components
        ├── config.lua
        ├── controller
        ├── en.lproj
        ├── extlib
        ├── jp.lproj
        ├── main.lua
        └── mySplashScreen.png

Kwik4 could not choose a folder for publishing, it was fixed to use “build4” folder, Kwik5 can select any photoshop files on your PC, and can publish to a App/book folder.

Solar2D Project > Select Book

Publish

selected psd files are publihsed to App/book folder

Active Document

You can publish images/codes from active document only.

Layer Groups

TODO Kwik4_1280x1920 to be renamed as page1

You can export images of each member of a layer group.

The default behavior is to publish one mergerd image of a layer group. You need to select a layer group to be unmereged

Select layer groups and then click Unmerge button.

If you want to cancel a layer group to be unmerged, Check it and click Cancel button. It will disapper from the list.

The source files and the images of each layer in a layer group are exported when published.

TODO fix the bug that unmerged group is not indexed at the right position in index.lua

local scene = require('controller.scene').new(sceneName, {
    name = "kwik4_1280x1920",
    layers = {
          {  bg={} },
          {  copyright={} },
          {  star={} },
          {  hello={} },
          {  mycircle={} },
          {  myrect={},
          {  GroupA={
                { SubA = {} },} },
    },

Step2 - Solar2D

Solar2D Simulator

you have published .psd files in step 1. The images are the code for Solar2D are available in the output folder. Here you will load it to Solar2D simulator. You may build an app for iOS, Android, Desktop(mac or win).

Navigation

Open Simulator

Open main.lua in the output folder - Solar2D project

There is an option to open the simualtor in Publish command in Kwik UXP plugin when publish is completed.

TODO UXP::Publish to include swipe page or A/D keys for navigation ⭐️

kwiconfig.lua

debug = {enable = true, navigation= "swipe/keys"}

Auto open after publish in Kwik UXP plugin


Kwik Visual Code Editor

Linear Animation

Button

Action

Product build

main.lua

if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
  local lldebugger = loadfile(os.getenv("LOCAL_LUA_DEBUGGER_FILEPATH"))()
  lldebugger.start()
end

inspect = require("extlib.inspect")

local common = {
  commands = {"myEvent"},
  components = {
    -- "align",
    "myComponent",
    "thumbnailNavigation",
    "index"              -- this loads editor!
  }
}

Solar2D/components/common/index.lua

local editor = require("editor.index")
--
function _M:init(UI)
  editor:init(UI)
end
--
function _M:create(UI)
  editor:create(UI)
end
--
function _M:didShow(UI)
  editor:didShow(UI)
end
--
function _M:didHide(UI)
  editor:didHide(UI)
end
--
function _M:destroy(UI)
  editor:destroy(UI)
end
--
return _M

Tips
  1. Mac Corona simulator _TIPropertyValueIsValid warning

    2023-12-15 14:39:26.982 Corona Simulator[48574:2941064] _TIPropertyValueIsValid called with 4 on nil context!
    2023-12-15 14:39:26.982 Corona Simulator[48574:2941064] imkxpc_getApplicationProperty:reply: called with incorrect property value 4, bailing.
    2023-12-15 14:39:26.982 Corona Simulator[48574:2941064] Text input context does not respond to _valueForTIProperty:
    

    From Mac’s preference, add English input for keyboard, and Use it


Tools

Tools


Kwik Visual Code Editor

update the test-proj/Solar2D/component/editor to develop/Solar2D/tools/kwik-editor

This tool visually edits Soar2D/src files of a Kwik project and is harnessed by Pegasus http-server

↑ Select a layer or an event to review. You can edit values of properties

TODO each kwik component with default values

TODO ui components like checkbox/selectors … for each component ⭐️

react compnents on webview – can be shared with UXP panel which may send params via http to pegasus in kwik editor

current editPorpsTable.lua servers as plain table viewer & editor

TODO save/copy params to .http (YAML) for httpYac

TODO how to send text to clipboard from Solar2D ⭐️


Launcher

develop/Solar2D/tools/pegasus-launcher

You can open a solar2D project from VS Code with httpYac


Harness

Solar2D/server/tests

You can post params with httpYac in VS Code to a Solar2D project

TODO set Layer varaible with samples

TODO create models: animation, transition2, button …, and pegasus-receiver in kwik-editor ⭐️


Custom Code

you can put your own code(.lua) into commands/pageX/ and componets/pageX/layers folder.

Kwik Exporter traverses folders of Solar2D project to integrate your additons. Or you can manually add the file names to components/pageX/index.lua

for instance, myrect.lua calls myEvents.testHandler when user taps the rect.

The $weight in a comment line at the top is a variable for Kwik. A scene componet(layer or your custom code)with lower value will be placed upper. The top layer from Photoshop is zero. Then values are increases to until the background layer. For your custom code , you can use minus or positive with decimal. For example, myrect is -2, mycircle is -1. If you change weight values of custom code files, don’t forget to publish code again.

-- $weight=-2
--
local M = {}
--
function M:init(UI)
end
--
function M:create(UI)
  local sceneGroup = UI.scene.view
  local obj = display.newRect( sceneGroup, display.contentCenterX, display.contentCenterY-100, 100, 100 )
  obj:setFillColor(0.2,0.2,0.2);

  obj:addEventListener("tap", function()
    UI.scene:dispatchEvent({
      name = "myEvents.testHandler",
      UI = UI
    })
  end)
end
--
function M:didShow(UI)
end
--
function M:didHide(UI)
end
--
function  M:destory()
end
--
return M

myEvents.testHandker.lua

local instance = require("commands.kwik.baseCommand").new(
  function (params)
    local UI    = params.UI

    print("commands.myEvents.testhander")

    UI.scene:dispatchEvent({
      name = "myAction",
      UI = UI
    })

  end
)
--
return instance

You can find your custom code are inserted in components/pageX/index.lua. The layers are sorted internally by values of $weight variable.

local sceneName = ...
--
local model = {
    name = "page01",
    components = {
      layers = {
        {bg = {}},
        {mycircle={}},
        {myrect={}}
      },
        audios = {},
        groups = {},
        others = {},
        timers {},
        variables = {}
    },
    commands = {
      "myAction",
      "myEvnets.testHandler"
    },
    onInit = function(scene) print("onInit") end
}

--
local scene = require('components.kwik.scene').new(sceneName, model)

return scene

generate_scene_index tool

  1. create .lua for commands or components

  2. run the follwoing tool to update scenes/pageX/index.lua to append the new .lua to the index.lua

    /develop/Solar2D/tools/generate_scene_index is a Solar2D application. You can open the main.lua in Solar2D simulator.

    the table in the index.lua is updated by iterating files in editor.template/components and template/commands



Audacity

https://manual.audacityteam.org/man/label_tracks.html


httpYac REST API

httpYac Restful API

Representational State Transfer (REST) model for a programming framework

server/tests

PUT

you can create a lua file and index.lua will be updated too


Unit Test

Unit Test

  1. components/editor/index.lua

    local unitTestOn = true
    
  2. components/editor/tests/index.lua

    require "extlib.lunatest"
    local M = {
      run = function (props)
        print("============ lunatest =============")
        lunatest.suite("components.editor.tests.suite_controller")
        lunatest.suite("components.editor.tests.suite_selector", props)
        lunatest.run()
        print("============   end    =============")
      end
    }
    return M
    

Examples

cmponents/editor/tests/suite_controller.lua

it tests editor/controller/index.lua

module(..., package.seeall)

function suite_setup()
  controller = require "components.editor.controller.index"
  files = {}
  updatedScene = nil
end

function setup()
  book = "bookFree"
  page = "page1"
  tool = "interaction"
  layer = "butWhite"  -- Update scenes.components.layers.butWhite with the props
  class = "button"
  props = {name="helloBTN", class="button", kind="tap", actionName = "onClick", over="helloOver", btaps = 1, mask=""}
  scene = {
    name = "canvas",
    components = {
      layers = {
          {  back={
                            } },
          {  butBlue={ class={"button"}, {A={}}, {B={}}
                            } },
          {  butWhite={
                            } },
      },
      audios = {  },
      groups = {  },
      timers = {  },
      variables = {  },
      others = {  }
    },
    commands = { "blueBTN" },
    onInit = function(scene) print("onInit") end
  }
end

function teardown()
end

function test_render()
  local dst = controller:render(book, page, layer, tool, class, props)
  assert_string(dst, "fail")
  --
  local path = system.pathForFile(dst, system.TemporaryDirectory )
  local file = io.open( path, "r" )
  assert_userdata(file, "fail")
  files[#files + 1] = dst
end

cmponents/editor/tests/suite_selector.lua

automatically it loads eventOne.json in page1

 local M = {}

 local selectors
 local UI
 local bookTable
 local pageTable

 function M.init(props)
   selectors = props.selectors
   UI        = props.UI
   bookTable = props.bookTable
   pageTable = props.pageTable
 end

 function M.suite_setup()
   selectors.projectPageSelector:show()
   selectors.projectPageSelector:onClick(true)
   --
   UI.scene.app:dispatchEvent {
     name = "editor.selector.selectApp",
     UI = UI
   }
   -- appFolder = system.pathForFile("App", system.ResourceDirectory) -- default
   -- useTinyfiledialogs = false -- default
   ---
   bookTable:commandHandler({book="bookFree"}, nil,  true)
   pageTable:commandHandler({page="page1"},nil,  true)
 end

 function M.setup()
 end

 function M.teardown()
 end

 -- function M.test_component()
 --     selectors.projectPageSelector:show()
 -- end

 function M.test_action()
   selectors.componentSelector:show()
   selectors.componentSelector:onClick(true,  "actionTable")
   UI.editor.currentAction  = "eventOne"
   UI.scene.app:dispatchEvent {
     name = "editor.action.selectAction",
     UI = UI
   }
 end

 return M

Visual Studio Code

VS Code

for mac

https://code.visualstudio.com/docs/setup/mac

URLs


### extensions
the following extensions helps your coding with Solar2D simulator.

- Solar2D Autocomplete
- Solar2d Companion for Visual Studio Code
- Local Lua Debugger
- Run Terminal Command


## Local Lua Debugger

For debugging with Local Lua Deubgger, launch.json should be placed in your workspace folder. For instance,














Assuming Kwik projects are located in ~/Kwik folder and this is your workplace folder of Visual Studio Code. launch.json will be created under .vscode/ folder

- ~/Kwik/.vscode/launch.json

  > Corona Simulator.app is specified and ${file} is in the last argurment

  this is for Mac

  ```json
    "version": "0.2.0",
    "configurations": [
      {
        "name": "Debug",
        "type": "lua-local",
        "request": "launch",
          "program": {
            "command": "/Applications/Corona/Corona Simulator.app/Contents/MacOS/Corona Simulator",
          },
          "args": [
            "-no-console"
            "YES"
            "-debug"
            "1"
            "-project"
            "${file}"
          ]
      }
    ]
  }

for Windwos

{
"version": "0.2.0",
"configurations": [{
    "name": "Debug",
    "type": "lua-local",
    "request": "launch",
      "program": {
        "command": "C:\\Program Files (x86)\\Corona Labs\\Corona\\Corona Simulator.exe",
      },
      "args": [
        "/no-console",
        "/debug",
        "${file}"
      ]
  }]
}

Run Terminal Command

this is a utility to run terminal commands. You can select a folder and right click opens a dialog to select a terminal command

You can add a terminal command to settings.json

for Mac

{
  "command":"\"/Applications/Corona/Corona Simulator.app/Contents/MacOS/Corona Simulator\" ./ -no-console YES",
 "name":"Solar2D"
},

Implementation

TODO

editor/controller/save.lua
editor/parts/controller/properties/save.lua

implementation Details

the following functions in editor.controller.index class are mainly used for CRUD of json/lua files in select, save, delete etc. See editor.controller.save.

separate controllers for the audio, group, timer and variable components are implemented i.e

### select (read)

See editor.parts.controller directory. selectXXX lua reads json and displays a UI table. The data is passed by nanostore such as bookStore:set, pageStore:set.

nanostore is used as an experimental usage

  1. selectApp.lua

    local appFolder = params.appFolder or system.pathForFile( "App", system.ResourceDirectory )
    
    if params.useTinyfiledialogs then
      appFolder = tfd.selectFolderDialog({
        title = "select App folder",
        default_path = path
      })
      -- print("", appFolder)
    end
    
    if success then
      local books = {}
      for file in lfs.dir( appFolder ) do
        if util.isDir(file) then
          -- print("",  "Found file: " .. file )
          -- set them to nanostores
          if file:len() > 3 and file ~="kwikEditor" then
            table.insert(books, {name = file, path= util.PATH(appFolder.."/"..file)})
          end
        end
      end
      if #books > 0 then
        UI.editor.bookStore:set(books)
      end
    
  2. selectBook.lua

    local path =system.pathForFile( "App/"..bookName.."/models", system.ResourceDirectory)
    UI.editor.currentBook = bookName
    local success = lfs.chdir( path ) -- isDir works with current dir
    if success then
      local pages = {}
      for file in lfs.dir( path ) do
        if util.isDir(file) then
          -- set them to nanostores
          if file:len() > 3 and file ~='assets' then
            table.insert(pages, {name = file, path= util.PATH(path.."/"..file)})
          end
        end
      end
      if #pages > 0 then
        UI.editor.pageStore:set(pages)
      end
    end
    
  3. selectPage.lua

    if params.page:len() > 0 and UI.page ~= params.page then
      local app = App.get()
      app:showView("components." .. params.page .. ".index", {effect = "slideDown"})
    end
    


create a new component from toolbar


UI.scene.model

UI.scene.model is set when selectPage is called

local scene = require('controller.scene').new(sceneName, {
name = "page1",
components = {
  layers = {
        {  bg={
                          } },
        {  gotoBtn={ --class={"animation"}
                          } },
        {  title={ class={"linear"} } },
  },
  audios = {},
  groups = {},
  timers = {},
  variables = {},
  page = { }
},
commands = { "eventOne", "eventTwo", "act01" },
onInit = function(scene) print("onInit") end
})

util.read() function parses “App/”..book.."/models/"..page .."/index.json"

...
ret.layers = parser(decoded)
...
setFiles(ret.audios, "/audios/short")
setFiles(ret.audios, "/audios/long")
setFiles(ret.groups, "/groups")
setFiles(ret.commands, "/commands")
return ret
{
  layers = {
    {name = "layerOne", parent="", children = {
        {name="childOne}, parent="layerOne", children = {}}
      }
    },
    {name = "layerTwo", parent="", children = {}},
  },
  audios = {}
}

save (write)

CRUD operations on json/lua files are mainly for commands and components in a selected page

the editor does not support to create a new entry of App, Book, Page. The properties can be modified for App, Book, Page.

layer names must be unique in a scene model.

```lua
util.createIndexModel(UI.scene.model, UI.editor.currentLayer, classname)
```

```lua
scene = {
  name = "canvas",
  components = {
    layers = {
        {  back={
                          } },
        {  butBlue={ class={"button"}, {A={}}, {B={}}
                          } },
        {  butWhite={
                          } },
    },
    audios = {  },
    groups = {  },
    timers = {  },
    variables = {  },
    others = {  }
  },
}
```
- render App.booxX.components.pageX.index.lua
- save json
local name = ...
local parent, root = parent_root(name)
local util = require("editor.util")
local json = require("json")
--
-- save command performs on one entry.
-- If user switch to another entry without saving, the previous change will be lost.
--
local instance =
  require("commands.kwik.baseCommand").new(
  function(params)
    local UI = params.UI
    local props = params.props
    local tool = UI.editor:getTool(props.class) -- each tool.contoller can overide render/save. So page tools of audio, group, timer should use own render/save
    if tool then
      local files = {}
      local toolName = UI.editor:getToolName(props.class)
      local filename = props.name
      local classname = props.class:lower()
      -------------
      -- save lua
      files[#files+1] = tool.controller:render(UI.editor.currentBook, UI.page, UI.editor.currentLayer, toolName, classname, props)
      -----------
      --- save json
      local decoded = params.decoded or {}
      decoded[props.index] = props
      --
      files[#files+1] = tool.controller:save(UI.editor.currentBook, UI.page, UI.editor.currentLayer,toolName, decoded)
      -----------
      --- Update components/pageX/index.lua model/pageX/index.json
      local updatedModel = util.createIndexModel(UI.scene.model, UI.editor.currentLayer, classname)
      files[#files+1] = tool.controller:renderIndex(UI.editor.currentBook, UI.page, updatedModel)
      files[#files+1] = tool.controller:saveIndex(UI.editor.currentBook, UI.page, UI.editor.currentLayer,classname, updatedModel)
      ----------
      -- publish
      util.executePubish(files)
    else
      print("tool not found for", props.class)
    end
  end
)
--
return instance

assets

media files of audio, layer replacements(video, spritesheet, particle, syncText, web) in App/bookX/assets folder are indexed with linked layers in assets.json

So tool.controller:save() also performs a write operation on assets.json


Adding A Component

how to add a component

editor/index.lua and baseTable.lua handles user’ selection from xxxTable commands/selectors/selectXXX.lua reads a json

selectors.lua handles to read values of UI.scene.model.components for xxxTable.

  1. editor/models.lua

    this defines layer related components. it does not include audio, group, timer, variables and action.

      local models = {
        {
            name = "Animations",
            icon = "toolAnim",
            tools = {
              {name = "Linear", icon = "animLinear"},
              {name = "Blink", icon = "animBlink"},
              {name = "Bounce", icon = "animBounce"},
              {name = "Pulse", icon = "animPulse"},
              {name = "Rotation", icon = "animRotation"},
              {name = "Shake", icon = "animShake"},
              {name = "Switch", icon = "animSwitch"},
              {name = "Filter", icon = "animFilter"},
              {name = "Path", icon = "animPath"},
            },
            id = "animation"
         },
        ...
    

    editor/index.lua loads it. The id is used to load a module for editing a component like

    animation = require("editor.animation.index")
    

    For audio, group, timer and variables, the icons and the modules are loaded with their table.lua for instance, groupTable.lua

    local name = ...
    local parent = name:match("(.-)[^%.]+$")
    
    local Props = {
      name = "group",
      anchorName = "selectGroup",
      icons = {"Groups", "Trash"},
      id = "group"
    }
    
    local M = require(parent.."baseTable").new(Props)
    return M
    

    action’ icon/module ared loaded with action/index.lua. It is called by editor/index.lua

      local actionIcon = muiIcon:create {
      icon = {"actions_over", "actions_over","actions_over"},
      text = "",
      name = "action-icon",
      x = display.contentCenterX/2 + 42,
      y = -2,
      -- y = (display.actualContentHeight - display.contentHeight)/2 -2,
      width = 22,
      height = 22,
      fontSize =16,
      listener = function()
        self.isVisible = not self.isVisible
        if self.isVisible then
          self:show()
        else
            self:hide()
          end
        end,
        fillColor = {1.0}
      }
    
      UI.editor.actionIcon = actionIcon
    
  2. editor/index.lua

    Add {name = “selectXXX”, btree= “load xxx”}

    M.commands = {{name="selectApp", btree=nil},
      {name="selectBook", btree="load book"},
      {name="selectPage", btree="load page"},
      {name="selectLayer", btree="load layer"},
      -- {name="selectAction", btree=""},
      {name="selectTool", btree="editor component"},
      -- {name="selectActionCommand", btree=""}
      {name="selectAudio", btree="load audio"},
      {name="selectGroup", btree="load group"},
      -- {name="selectTimer", btree="load timer"},
      -- {name="selectVariable", btree="load variable"},
      -- {name="selectVideo", btree="load video"},
    }
    
  3. new xxxTable out of baseTable.lua

    write like this

    local name = ...
    local parent = name:match("(.-)[^%.]+$")
    
    local Props = {
      name = "group",
      anchorName = "selectGroup",
      icons = {"Groups", "Trash"},
      id = "group"
    }
    
    local M = require(parent.."baseTable").new(Props)
    return M
    

    this baseTable fires “load xxx” with a selected entry in xxxTable when user clicks it

    baseTable.lua

      tree.backboard = {
        show = true,
        class = target.class
      }
    
      tree.backboard[self.name] = target[self.name],
    
      tree:setConditionStatus("select component", bt.SUCCESS, true)
      tree:setActionStatus("load "..self.name, bt.RUNNING, true)
      tree:setConditionStatus("select "..self.name, bt.SUCCESS)
    

    then next, this tree action (load xxx) is called back. It is linked to selectXXX command by editor/index.lua. commands/selectors/selectXXX to load a json of a selected entry

  4. selectors.lua

    this is a view model. You see each command to be displayed as a button. It is a anchorName to xxxTable

    Add set store name “xxxTable” and btree = “select component”

    M:create(UI)

      self.componentSelector =
        selectorBase.new(
        UI,
        33,
        -2,
        {
          {label = "Layer", command = "selectLayer", store = "layerTable", filter = true, btree = "select layer"},
          {label = "Audio", command = "selectAudio", store = "audioTable", btree = "select component"},
          {label = "Group", command = "selectGroup", store = "groupTable", btree = "select component"},
          {label = "Timer", command = "selectTimer"},
          {label = "Var", command = "selectVariable"},
          {label = "Action", command = "selectAction", store = "actionTable"}
        },
        "toolLayer",
        selectLayerFilter,
        propsTable,
        propsButtons
      )
    

    M:didShow()

    Add xxxStore:set

        UI.editor.layerStore:set({})
        UI.editor.audioStore:set({})
        UI.editor.actionStore:set({})
        UI.editor.groupStore:set({})
        UI.editor.timerStore:set({})
        UI.editor.variableStore:set({})
        --
        if storeTable then
          -- should we show the last secection?
          if storeTable == "layerTable" then
            UI.editor.layerStore:set(UI.scene.model.components.layers)
          elseif storeTable == "audioTable" then
            print(storeTable)
            UI.editor.audioStore:set(UI.scene.model.components.audios)
          elseif storeTable == "groupTable" then
            print(storeTable)
            UI.editor.groupStore:set(UI.scene.model.components.groups)
          elseif storeTable == "actionTable" then
            UI.editor.actionStore:set(UI.scene.model.commands)
          end
      ...
    
  5. commands/selector/selectXXX

    write code to read json and set it to propsTable

    local command = function (params)
      local UI    = params.UI
      local xxx =  params.xxx or ""
    
      local path = system.pathForFile( "App/"..UI.editor.currentBook.."/models/"..UI.page .."/audios/"..params.class.."/"..xxx..".json", system.ResourceDirectory)
    
        local decoded, pos, msg = json.decodeFile( path )
        if not decoded then
          print( "Decode failed at "..tostring(pos)..": "..tostring(msg), path )
        else
          print( "props is decoded!" )
          UI.editor.propsStore:set(decoded)
          propsButtons:show()
        end
    
  6. for controllers for components are registered either in index.lua or selectors.lua or buttons.lua

    for instance, action/selectors.lua

    M.commands = {"selectAction", "selectActionCommand"}
    ---
    function M:init(UI, toggleHandler)
      local app = App.get()
      for i = 1, #self.commands do
        app.context:mapCommand(
          "editor.action." .. self.commands[i],
          "editor.action.controller." .. self.commands[i]
        )
      end
      self.togglePanel = toggleHandler
    end
    

    for animation/buttons.lua

    M.commands = {"create", "delete", "save", "cancel", "copy", "paste"}
    ---
    function M:init(UI, toggleHandler)
      local app = App.get()
      for i = 1, #self.commands do
        app.context:mapCommand(
          "editor.anim." .. self.commands[i],
          "editor.animation.controller." .. self.commands[i]
        )
      end
      self.togglePanel = toggleHandler
    end
    
  7. editor.animation.controller

each contoller of components is based on editor.controller.index

local M = require("editor.controller.index").new("animation")
 ...
 ...

return M

As of the base class, editor.controller.index has render(), save(), renderIndex(), saveIndex(), read() … and component’controllers can override them for their own.

for a component which uses a media file in assets, don’t forget to update assets.json too


Asset Editor

Asset Editor

model


Create

user action

assetTable lists media files and use can select one and then click then the icon on the left top corner for creating a component.

When clicking the icon, editor pass the media file proprs to a layer component tool


Save

A media file is always linked with one of components. So assets.json is edited by the controller of a component that handles one of media type

asset.controller.save is called by a component’s controller


Audio Editor

Audio Editor

Read

Save


Behavior Tree

Behavior Tree in Editor

This is experimental. A Behavior tree implemetation enables a conditional call for an action node. If conditions are not satisfied for a button, the associated action to the button will not be executed when user clicks it

editor/controller/BTree/selectors.tree

BTree calls BThandler when actionNode is activated. The parameters are stored in backboard.


Class Diagrams

Class Diagrams

TBI for saving a component with a media file in assets.


Animation Editor
classDiagram

editorIndex
editorIndex *-- menu : 1. User clicks Layer
commands o-- selectBook
commands o-- selectPage
commands o-- selectLayer
commands o-- selectTool


class selectors{
  - List: App, Book,Page
  - List: Layer, Audio, Group, Timer ..
  + projectPageSelector
  + componentSelector
  - componentHandler()
}

class selectorBase{
  + selectorIcon
  + entries
  + onClick()
}

selectors <|..selectorBase: 1.1 onClick calls componentHandler

menu *-- selectors

selectors ..> store :1.2 commandHandler sets Layer entries

class commands {
  + page
  + book
  + layer
  * class
  store.set()
}

class selectTool {
  controller:command()
}

class editorAnimation {
  + layerTable as selectbox
  + controlbox
  + buttons
  + controller
}

class layerTable {
  + name
  + class entries: animation, interaction, replacement
  create() displays name and class entries
  oncllick() name for propsTable, animation for editorAnimation
}

class layerTableCommands{
  + commandHandler
  + commandHanderClass
}

class selectLayer {
  propsTable:setValue(decoded)
  propsTable:show()
  propsButtons:show()
}

class propsTable {
  can be editable
}

propsTable *-- buttons

selectLayer ..>propsTable

layerTable *--layerTableCommands

class controller {

  selectbox.classEditorHandler()
  -> reset()
  -> setValue()
  -> redraw()

  command()
    -> util.decode(params)
    -> selectbox:setValue(decoded)
    -> controlbox:didHide(UI)
    -> controlbox:destroy(UI)
    -> controlbox:init(UI)
    -> controlbox:setValue(decoded)
    -> controlbox:create(UI)
    -> controlbox:didShow(UI)
    -> controlbox:show()
    -> onCompletebox:show()
    -> buttons:show()
}


editorAnimation *-- layerTable

editorAnimation <|.. controller

layerTable <|.. baseTable : create() is overrided in layerTable

editorAnimation *-- conrolbox

conrolbox <|.. baseProps

class baseTable {
  + entries
  commandHandler()
  store.listener()
  render()
}

class BTree{
  setCondition()
  setActionStatus()
}

layerTableCommands ..> BTree : 2. User clicks a animation entry <br> 2.1 BTree select animation TRUE

BTree ..> commands : 2.2. load animation (Activated) <br>2.3.1 selectTool for animation class

commands ..> selectTool: 2.5. select animation
selectTool ..> controller: 2.5 calls command() to display animationEditor

store ..> baseTable : 1.3. calls listenr in baseTable

class selectbox{
  + setTemplate()
  + commandHandler()
}

editorAnimation *-- selectbox : User clicks an animation entry

selectbox --> controller : commandHandler calls classEdtiorHandler

Group Editor
classDiagram

commands o-- selectLayer
commands o-- selectGroup


selectors ..> store

selectGroup --> _selectGroup

class _selectGroup{
  <<Controller>>
  decodede = util.read
  layersbox store.set(decodede)
  layersTable store.set(decoded)
  return instance.new()
}

class add{
    <<Controller>>
  layersbox:set(decodede)
  layersTable:set(decoded)
}

class remove{
    <<Controller>>
  layersbox:set(decodede)
  layersTable:set(decoded)
}

class editorGroup {
  + groupTable as selectbox
  + controlbox
  + buttons
  + layersbox
  + groupTable
}

class groupTable {
  + groups
  oncllick()
}

class layersbox {
  + layers
  setValue() override
}

layersbox <|.. targetbox
layersbox <|.. targetboxMulti
targetboxMulti --> util

class targetboxMulti{
  commandHandler() override
}

class util {
  + setSelection()
}

class layersTable {
  + layers
  scrollView = widget.newDragItemsScrollView()
}

class dragitemscrollview{
  <<ext>>
}

layersTable --> dragitemscrollview
layersTable --> util

groupTable ..> BTree

editorGroup *-- groupTable

editorGroup *.. layersbox

editorGroup *.. layersTable

editorGroup o.. add
editorGroup o.. remove
editorGroup o.. _selectGroup


groupTable <|.. baseTable

editorGroup *-- conrolbox

conrolbox <|.. baseProps


BTree ..> commands

commands ..> selectGroup
_selectGroup ..> layersbox

_selectGroup ..> layersTable

store ..> baseTable

class selectGroup{
  require(group.controller.selectGroup)
}

Timer Editor
classDiagram

editorIndex
editorIndex *-- menu : 1. User clicks Timer
commands o-- selectBook
commands o-- selectPage
commands o-- selectLayer
commands o-- selectTimer


class selectors{
  - List: App, Book,Page
  - List: Layer, Audio, Group, Timer ..
  + projectPageSelector
  + componentSelector
  - componentHandler()
}

class selectorBase{
  + selectorIcon
  + entries
  + onClick()
}

selectors <|..selectorBase: 1.1 onClick calls componentHandler

menu *-- selectors

selectors ..> store :1.2 commandHandler sets Timer entries

class commands {
  + page
  + book
  + layer
  * class
  store.set()
}

class selectTimer {
  controller:command()
}

class editorTimer {
  + timerTable as selectbox
  + controlbox
  + buttons
  + controller
}

class timerTable {
  oncllick()
}

class controller {
  command()
    - util.decode(params)
    - controlbox:didHide(UI)
    - controlbox:destroy(UI)
    - controlbox:init(UI)
    - controlbox:setValue(decoded)
    - controlbox:create(UI)
    - controlbox:didShow(UI)
    - controlbox:show()
    - onCompletebox:show()
    - buttons:show()
}

timerTable ..> BTree: 2. User clicks a timer entry <br> 2.1 BTree select timer TRUE

editorTimer *-- timerTable

editorTimer <|.. controller

timerTable <|.. baseTable

editorTimer *-- conrolbox

conrolbox <|.. baseProps

class baseTable {
  + entries
  commandHandler()
  store.listener()
  render()
}

class BTree{
  setCondition()
  setActionStatus()
}

BTree ..> commands : 2.2. load timer (Activated) <br> 2.3 menu/controller/selettor/selectTimer

commands ..> selectTimer: 2.5. select timer
selectTimer ..> controller: 2.5 calls command() to display timerEditor

store ..> baseTable : 1.3. calls listenr in baseTable

https://zenn.dev/taroman_zenn/articles/3c811b1245240d


Replacement Editor

Replacement Editor

listbox and listButtons for syncText and Spreadsheet

User can create mutliple sprites as seqeunce data from a spresheet(imageSheet) , and for Sync Audio & Text, one line of text consists of words.

These arrays of data are displayed by listbox.lua and add/save/delete/select are handled in replacement.controller.add, replacement.controller.save, replecment.controller.delete, replacment.controller.select respectively.

M.commands = {"delete", "save", "cancel", "select", "add"}

---
function M:init(UI)
  local app = App.get()
  for i = 1, #self.commands do
    app.context:mapCommand(
      "editor.replacement.list." .. self.commands[i],
      "editor.replacement.controller." .. self.commands[i]
    )
  end
end
--
// TBI a tool for imagesheet

  
      
      
      


  ref: https://kwiksher.com/doc/kwik_tutorial/animations/working_with_spritesheet/#b-if-you-are-using-an-optimized-file-from-texture-packer

  //TBI the sequence data is generated by frames' data in sheetInfo lua?
  //if asset.json's sheetInfo is text string, it is from texture packer

  ```
  {
   filename = "slots.png",
   sheetInfo = "slots.lua",
  }
  ```

  Kwik needs these entry in sequence data to create each sprite
  ```
  Name:bird
  Start Frame 1
  Frame Count 1
  Length 1 sec

  Name:cat
  Start Frame 2
  Frame Count 1
  Length 1 sec
  ```

  the table of frames is exported from Texturepacker

  ```lua
  frames = {
      {
          -- bird
          x=2,
          y=2,
          width=191,
          height=222,

          sourceX = 1,
          sourceY = 1,
          sourceWidth = 192,
          sourceHeight = 223
      },
  '''

//TBI editor.template.components.pageX.replacement.defaults.spritesheet
// the sheetInfo for a Same size frames sheet should be a table

Now
```
local M = {
  name = "spritesheet",
  class = "spritesheet",
  type  = "uniform-sized", -- TexturePacker, Animate
  controls = {
    filename = "imagesheet.png",
    sheetInfo = "spritesheet",
```

Update it as same as sheetInfo in assets.json
```
sheetInfo = {
    width = 188,
  height = 188,
  sheetWidth = 376,
  sheetHeight = 188,
},
```

save

with the listbox value, save for a replacement component is as normal as the other components. editor.controller.save is processed and retrives useClassEditorProps() in replacment.controller.index

// TBI? listbox getValue in userClassEditorProps // suite_page1_replacements.lua does not have unit tests for saving spreadsheet/video. It has “editor.replacement.list.save” though.


REST API

Rest API

doGet/doPost are handled with server/harness.lua, that can access UI table

function M:init(path, params)
	projRoot = path
  self.selectors = params.selectors
  self.UI        = params.UI
  self.bookTable = params.bookTable
  self.pageTable = params.pageTable
  self.layerTable = params.layerTable

end

function M:dispatchEevnt(eventName, params)
    self.UI.scene.app:dispatchEvent {
      name = eventName, -- "editor.selector.selectApp",
      UI = self.UI,
      params = params
    }
  -- selectApp
  --   returns books
  --
end

Transition2.page

App/Transition2 is a normal page of Solar2D composer. It does not have a model of index.lua. We coulld load Transition2.page with composer.gotoScene but this way does not work with kwik model with compoennts/commands.

So let’s define I/F.

httpYac

### for composer.gotoScene
GET /Transition2/page

### selectLayer
GET /Transition2/page/character

###
POST /Transition2/page/character/transition2.to

If index.lua is not found, it is a noraml composer page, and the following I/F are used for doGet and doPost

page.lua

function scene:getLayer(layerName)

function scene:getFunc(funcName)

PUT

PUT

See server/test/book01/

create a new book

PUT /newBook

template is located in editor/template


create a new page

PUT /newBook/page1

create a new layer

PUT /newBook/page1/imageOne

TODO: layer_image.lua to display text(layer name) if imageOne.png is not found in assets/pageX

attach a new class

PUT /newBook/page1/imageOne/?class=linear

events

PUT /newBook/commands/page1/eventOne

copy page

create page2 from page1.

PUT /newBook/page2?copyFrom=newBook/page1

REST Server

Rest Server

editor.pegasus

pegasus is a open-souce lua http-server

httpYac in vscode to send a REST request to editor.pegasus

graph LR

subgraph vscode
  httpYac
end

subgraph solar2D
  pegasus
  subgraph commands
    selectApp
    selectBook
    selectPage
    selectLayer
    selectTool[selectTool <br> new or update class props]
  end
  subgraph controllers_save
    updateIndex[index model <br> layer model]
    save[save .json]
    render[render lua]

    updateIndex -.-> render
    updateIndex -.-> save

  end
  subgraph nanosotres
    bookStore
    pageStore
    layerStore
  end
  subgraph UI
    bookTable
    pageTable
    layerTable
    subgraph animation
      selectBox
      contropProps
      pointABBox
      onCompletebox
    end
    subgraph audio
      selectBox_[selectBox]
      contropProps_[controlProps]
      onCompletebox_[onCompletebox]
    end
  end
  subgraph models
    subgraph runtime
      lua[pageX.index <br> <br> scene.components <br> - layers  <br> - audios <br><br> layerX.lua <br> layerX_anim.lua]
    end
    subgraph persistent
      json[models/pageX/.json]
      layerX.json
      layerX_anim.json
    end
  end
end

httpYac -.- pegasus
pegasus -. 1.onGet.->selectLayer
selectLayer -. read .- lua

lua -. 1.onGet set .-> layerStore
selectLayer -. show .- layerTable
layerStore -. 1.onGet subscribed data.-> layerTable

selectTool -. nanostores get .- layerStore
selectTool -.read class.- lua

save -. write .- json
save -. write .- layerX_anim.json
save -. write .- layerX.json

render -.write .- lua
pegasus -. 2-1.onPost class.->selectTool
pegasus -. 2-2.onPost save.->updateIndex
layerX_anim.json -.setValue .-> contropProps

lua -. read .- layerX.json
lua -. 2-1 onPost read .- layerX_anim.json

Selectors

Selectors


note: util.read() function parses “App/”..book.."/models/"..page .."/index.json"

...
ret.layers = parser(decoded)
...
setFiles(ret.audios, "/audios/short")
setFiles(ret.audios, "/audios/long")
setFiles(ret.groups, "/groups")
setFiles(ret.commands, "/commands")
return ret
{
  layers = {
    {name = "layerOne", parent="", children = {
        {name="childOne}, parent="layerOne", children = {}}
      }
    },
    {name = "layerTwo", parent="", children = {}},
  },
  audios = {}
}

Introduction

Introduction

Modified

UXP

Folder structure

Lua


New Features

UXP

Kwik Editor in Solar2D

animation, button, layer repleacements(spritesheet,video, particles) are editied with Kwik Editor in Solar2D.

It helpes to change values of components properties easily, or add/attach a component class to a layer visually for designers to do a no-code editing

Kwik5 is aimed to be a low code tool faster to work with .lua code easily and directly. See the next Lua section

kwik4kwik5
1. kwik’s component panel saves props to .kwk(XML)
2. Publish .lua from .kwk
1. UXP Publish code(.lua) as base
2. Create/modify .lua for animation, button .. with Kwik editor in Solar2D simulator

For developers, use a text editor to edit .lua directly

Lua

Kwik4 had .kwk xml to hold a properties/values of a Kwik project. Kwik5 uses .lua mainly

Bookstore


OLD

$5 a month 0 sponsors Get a Sponsor badge on your profile. Let’s start a project with Kwik

$10 a month 0 sponsors Support Kwik development, documentation and bug fixing

$25 a month 0 sponsors Access to new features before in advance of their becoming open source.

$50 a month 0 sponsors Pesonal support. About 3-4 hours in a week

$75 a month 0 sponsors Previous two tiers combined. Let’s discuss about new featuers of Kwik.

$100 a month 0 sponsors I will make significant effort to update or may be create a sample tutorial of your choosing.

$200 a month 0 sponsors Your issues and request hit the top of my list. You may ask for beta testers in our facebook group and issues are handled in a private repository for your beta testing.

$500 a month 0 sponsors This includes everything above, plus your app will be featured in our blog or video if you wish


New


Solar2D Linux

Solar2D Linux

ubuntu 22.10 kinetic

https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/releases

ubuntu 22.08 (Kinetic): Based on Debian 12 (Bookworm) GNOME 43

/Users/ymmtny/Documents/work/Solar2DTux/

platform/linux/buildx


bookworm uses wayland. raspi-config > Advanced to swich X11

sudo raspi-config nonint do_wayland W1

https://qiita.com/ktamido/items/82ed2f5bd324d4721096 バージョン0.7.0以降の WayVNC は RealVNC と互換性のある RSA-AES 認証が追加されたらしく、適切な証明書を生成すると、RealVNCから接続できる

https://docs.cse.lehigh.edu/xforwarding/xforwarding-mac/ https://stackoverflow.com/questions/65468655/vs-code-remote-x11-cant-get-display-while-connecting-to-remote-server

sudo apt update
sudo apt install x11-apps

java 8

AppImage


snap

error

pi@kwiksher-solar2d:~/projects/deploy-solar2d $ snap run --strace solar2d
/snap/strace-static/current/bin/strace: Cannot find user 'pi'
sudo apt update
sudo apt install snapd
sudo reboot

sudo snap install core
snap install hello-world
sudo snap install snapcraft --classic
sudo snap install lxd --channel=latest/stable

sudo snap remove –purge lxd

https://stackoverflow.com/questions/57525289/not-able-to-execute-lxd-lxc-commands-as-sudo

newgrp lxd
sudo usermod -aG pi lxd
lxd init  --minimal
snapcraft --use-lxd

sudo usermod -aG ymmtny lxd

sudo gpasswd lxd

snap install opy_latest_amd64.snap --dangerous
unsquashfs <file>.snap
  sudo apt update

  ln -s ../sysroot sysroot

  snap remove --purge lxd

  lxd init --minimal
  snapcraft --use-lxd

  snapcraft prime --shell-after --use-lxd

  snapcraft clean

  lxc ls
  lxc container
  lxc delete {name}

  snap install solar2d_2100.9999_arm64.snap --dangerous --devmode

  snap connections --all

The shorthand format will also produce the same result:

```
architectures:
  - amd64
```

snapパッケージング入門

sudo ifconfig usb0 down for windows with raspi4 type-c

https://blog.tstylestudio.com/2022/02/10/raspberrypi%E3%81%ABlxd%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/


flatpak

flatpak run --command=sh --devel <application-id>

deb

clang


docker buildx

debian ubuntu

jetson nano


gcc

The Bullseye default compiler is gcc 10, with Bookworm it’s gcc 12. clang-13 the latest clang release on Bullseye, clang-15 the latest on Bookworm.


kwiksher-solar2d.loca

  otg_mode=1
  dtoverlay=dwc2
  #dtoverlay=disable-wifi
modules-load=dwc2,g_ether

first_run.sh, Before the rm … command, add this:

```
cat << EOF > /etc/network/interfaces.d/usb0
auto usb0
allow-hotplug usb0
iface usb0 inet static
    address 192.168.2.2
    netmask 255.255.255.0
    gateway 192.168.2.1
EOF
```