The Lua Carousel "productivity suite"
Another new version of Lua Carousel today. Not because of any new bugs or features, but a few small changes to Carousel's primitives as I try to use them in scripts.
In particular, I've included the library for writing .wav files in the last post into Lua Carousel. Now the voice recorder should be much easier to get running.
But I'm getting ahead of myself. I want to show 4 different little programs today that illustrate primitives I often reach for in my programs: buttons, sliders, editors and audio recorders/players.
The first example illustrates how to create a button that does something when you press it. Here the yellow rectangle is a button:
This script requires 30 lines of code:
ui_state = {} shape = 'circle' function car.draw() ui_state.button_handlers = {} -- A -- draw a red shape color(1,0,0) if shape == 'circle' then circle('fill', 100, 100, 40) elseif shape == 'square' then rect('fill', 60,60, 80,80) end -- draw a yellow button that toggles the shape button(ui_state, 'toggle', { x=100-20,y=100+50+20,w=40,h=20, bg={r=1, g=1, b=0}, icon=function(p) color(0,0,0) rect('line', p.x,p.y, p.w,p.h) end, onpress1=function() if shape == 'circle' then shape = 'square' else shape = 'circle' end end, }) end function car.mousepressed(x,y, b) if mouse_press_consumed_by_any_button(ui_state, x,y, b) then return end end
Add a line of code (A) to `car.draw` and a second block to `car.mousepressed`, and now you can add as many buttons as you want by calling `button` in `car.draw`.
Example 2 illustrates how to create a slider that does something as you slide it left and right:
This requires another 30 lines of code:
ui_state = {} radius=40 function car.draw() ui_state.slider_handlers = {} -- A -- draw a red circle color(1,0,0) circle('fill', 100, 100, radius) -- draw a blue slider to adjust its size slider(ui_state, 'radius', { fg={r=0, g=0, b=1}, x0=60, x1=140, -- left and right limit y0=200, -- sliders are always horizontal w=20, h=20, -- size of the knob on the slider -- what the slider does lo=10, hi=100, -- min/max permitted radius value=radius, -- where the knob is placed update=function(v) radius=v end, -- what moving the knob does }) end function car.mousepressed(x,y, b) if mouse_press_consumed_by_any_slider(ui_state, x,y, b) then return end end function car.update() update_sliders(ui_state, love.mouse.getX()) end function car.mousereleased(x,y, b) ui_state.selected_slider = nil end
As before, add a line of code (A) to `car.draw` and some blocks to `car.mousepressed`, `car.mousereleased` and `car.update` (the last giving the slider its effect). Now you can add as many sliders as you want by calling `slider` in `car.draw`.
Example 3 is a text editor:
This takes 40 lines of code:
e = edit.initialize_state( Menu_bottom+10, 100, -- top and bottom 50, 150, -- left and right g.getFont(), Font_height, Line_height) -- use default font e.filename = 'example.txt' load_from_disk(e) Text.redraw_all(e) function car.draw() color(0.6,0.6,0.6) -- border with 5px padding rect('line', e.left-5, e.top-5, e.width+5*2, (e.bottom+5)-(e.top-5)) edit.draw(e, {r=0, g=0.6, b=0.6}) -- text color end function car.keychord_press(chord, key) edit.keychord_press(e, chord, key) end function car.text_input(t) edit.text_input(e, t) end function car.key_release(key) edit.key_release(e, key) end function car.mouse_press(x,y, b) edit.mouse_press(e, x,y, b) end function car.mouse_release(x,y, b) edit.mouse_release(e, x,y, b) end function car.update(dt) edit.update(e, dt) -- mainly to periodically autosave end function car.quit() edit.quit(e) -- make sure to save end
Again, the code consists of a few lines in various handlers. This is the exact editor used to build Carousel, and it's also heavily used in all my other forks. A hundred automated tests ensure it's working every time Carousel starts up. It supports file loading and saving, copy/paste, find, undo/redo, though some features currently don't work on mobile devices. For example, there's no way to scroll on my phone since I don't have arrow keys. I still need to package up Carousel's scrollbars so scripts can easily use them.
(One thing you might have noticed is that the handlers have slightly different names. The editor supports keychords like `C-a` for ctrl-a, `M-f` for alt-f, `M-S-left` for alt+shift+left arrow, etc. There's a new handler to easily respond to keychords even outside the editor. But `keychordpressed` felt like a mouthful so I ended up creating a whole parallel set of names: mouse_press, mouse_release, mouse_wheel_move, keychord_press, key_release, text_input. You can use either these names or the standard ones if you're used to them.)
(Correction: on 2024-02-16, Carousel version bf added an argument to `edit.initialize_state` for the font to use with an editor. To keep the above example working, I added the argument `g.getFont()`. I hope not to introduce further incompatibilities.)
Moving on, the final example involves recording and playing audio. This is a much simpler variant of the previous post. Unfortunately this still won't work on iOS, and on Android you'll need to grant LÖVE access to your microphone. Press the orange button to record, the red button to stop recording, and the green button to play what you recorded:
This script requires 50 lines of code, mostly to describe the 3 buttons:
ui_state = {} -- initialize recorder and player local devices = love.audio.getRecordingDevices() if #devices == 0 then error('no recording devices') end recording_device = devices[1] recording = nil playing_source = nil function car.draw() ui_state.button_handlers = {} -- orange button to start recording button(ui_state, 'record', { x=50, y=50, w=60, h=40, bg={r=1, g=0.6, b=0}, onpress1 = record, }) -- red button to stop recording button(ui_state, 'stop', { x=120, y=50, w=60, h=40, bg={r=1, g=0, b=0}, onpress1 = stop_recording, }) -- green button to play what we recorded button(ui_state, 'play', { x=190, y=50, w=60, h=40, bg={r=0, g=1, b=0}, onpress1 = play, }) end function record() recording_device:start(10*8000, 8000) playing_source = nil end function stop_recording(recording_idx) recording = recording_device:getData() recording_device:stop() end function play(recording_idx) playing_source = audio(recording, 'static') playing_source:play() end function car.mousepressed(x,y, b) if mouse_press_consumed_by_any_button(ui_state, x,y, b) then return end end
The core is the dozen or so lines near the bottom, using a `RecordingDevice` to control the microphone, `audio` to control the speaker and `save_wav`, now included in Lua Carousel, which takes a filename and a `SoundData` to save in it.
I'll stop here. Hopefully this has sparked some ideas for combining these primitives to help you in your life. As always, I'm happy to answer questions if something is unclear.
If you try pasting any of these programs into Lua Carousel, remember to first run the abbreviations on one of the example screens. Or if you've deleted that screen, here are the abbreviations I used in this post:
g = love.graphics circle, rect = g.circle, g.rectangle color = g.setColor audio = love.audio.newSource
Correction: An earlier version of this post didn't specify the second argument to `audio` (love.audio.newSource). It's not required in practice, but the documentation doesn't mention a default value, and I've since seen strange distracting errors if I run this program without granting LÖVE access to the microphone. Just always include the second arg.
Files
Get Lua Carousel
Lua Carousel
Write programs on desktop and mobile
Status | In development |
Category | Tool |
Author | Kartik Agaram |
Tags | LÖVE |
More posts
- New version after 3 days28 days ago
- New version after 40 days31 days ago
- Turn your phone or tablet into a chess clock44 days ago
- Guest program: bouncing balls61 days ago
- New version after 3 months, and turtle graphics71 days ago
- Interactively zooming in to the Mandelbrot set on a touchscreen90 days ago
- A little timer app91 days ago
- New version after 4 monthsJun 28, 2024
- Visualizing the digits of πMay 04, 2024
Leave a comment
Log in with itch.io to leave a comment.