119 lines
6.0 KiB
Go
119 lines
6.0 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"image"
|
||
"image/color"
|
||
"math"
|
||
"time"
|
||
|
||
"github.com/egoist/mygo/ui"
|
||
|
||
mujica "mujicaui"
|
||
"mujicaui/theme"
|
||
)
|
||
|
||
const mediaCategory = "Media"
|
||
|
||
var mediaPages = []page{
|
||
{ID: "341", Name: "ImageCropper", ZhName: "图片裁剪", Category: mediaCategory, Demo: imageCropperDemo, Code: imageCropperCode,
|
||
Keys: "方向键移动裁剪框;Shift+方向键缩放;R / Shift+R 向右 / 向左旋转。"},
|
||
{ID: "342", Name: "ImageAnnotator", ZhName: "图片标注", Category: mediaCategory, Demo: imageAnnotatorDemo, Code: imageAnnotatorCode,
|
||
Keys: "[ / ] 切换选中标注;方向键移动;Delete 删除;Escape 取消选择。"},
|
||
{ID: "343", Name: "ImageThumbnail", ZhName: "缩略图", Category: mediaCategory, Demo: imageThumbnailDemo, Code: imageThumbnailCode,
|
||
Keys: "Tab 聚焦;Enter / Space 激活。"},
|
||
{ID: "344", Name: "Lightbox", ZhName: "灯箱", Category: mediaCategory, Demo: lightboxDemo, Code: lightboxCode,
|
||
Keys: "← / → 切换;Home / End 首尾;Escape 关闭并恢复焦点。"},
|
||
{ID: "345", Name: "VideoPlayer", ZhName: "视频播放器", Category: mediaCategory, Demo: videoPlayerDemo, Code: videoPlayerCode,
|
||
Keys: "画面聚焦时 Space / K 播放暂停,← / → 跳转 5 秒,F 全屏,C 字幕。"},
|
||
{ID: "346", Name: "VideoScrubber", ZhName: "视频时间轴", Category: mediaCategory, Demo: videoScrubberDemo, Code: videoScrubberCode,
|
||
Keys: "← / → 跳转一个步长;Home / End 开头结尾。"},
|
||
{ID: "347", Name: "VideoThumbnailStrip", ZhName: "缩略帧条", Category: mediaCategory, Demo: videoStripDemo, Code: videoStripCode,
|
||
Keys: "Tab 聚焦帧条;← / → 上下帧;Home / End 首尾帧。"},
|
||
{ID: "348", Name: "AudioPlayer", ZhName: "音频播放器", Category: mediaCategory, Demo: audioPlayerDemo, Code: audioPlayerCode,
|
||
Keys: "Space 播放或暂停;进度条聚焦时 ← / → 跳转 5 秒。"},
|
||
{ID: "349", Name: "AudioWaveform", ZhName: "音频波形", Category: mediaCategory, Demo: audioWaveformDemo, Code: audioWaveformCode,
|
||
Keys: "← / → 跳转 5 秒;Shift+← / → 调整选区;Escape 清除选区。"},
|
||
{ID: "350", Name: "AudioSpectrum", ZhName: "音频频谱", Category: mediaCategory, Demo: audioSpectrumDemo, Code: audioSpectrumCode,
|
||
Keys: "Tab 聚焦;← / → 逐个读出频带;Home / End 首尾。"},
|
||
{ID: "351", Name: "VolumeControl", ZhName: "音量控制", Category: mediaCategory, Demo: volumeDemo, Code: volumeCode,
|
||
Keys: "← / → 音量 ±5%;Home / End;M 静音。"},
|
||
{ID: "352", Name: "PlaybackSpeedControl", ZhName: "倍速选择", Category: mediaCategory, Demo: speedDemo, Code: speedCode,
|
||
Keys: "Enter / Space 打开;↑ / ↓ 移动;Enter 确认;Escape 关闭。"},
|
||
{ID: "353", Name: "MediaControls", ZhName: "媒体控制", Category: mediaCategory, Demo: mediaControlsDemo, Code: mediaControlsCode,
|
||
Keys: "Tab 进入;← / → 在按钮间移动;Enter / Space 按下。"},
|
||
{ID: "354", Name: "Playlist", ZhName: "播放列表", Category: mediaCategory, Demo: playlistDemo, Code: playlistCode,
|
||
Keys: "↑ / ↓ 选择;Enter 播放;Alt+↑ / ↓ 移动;Delete 移除;可拖动排序。"},
|
||
{ID: "355", Name: "SubtitleEditor", ZhName: "字幕编辑器", Category: mediaCategory, Demo: subtitleDemo, Code: subtitleCode,
|
||
Keys: "Tab 在一行的开始、结束、文本、删除间移动;时间格式 m:ss.mmm。"},
|
||
{ID: "356", Name: "ScreenRecorderControls", ZhName: "录屏控制", Category: mediaCategory, Demo: recorderDemo, Code: recorderCode,
|
||
Keys: "Tab 在按钮间移动;Enter / Space 按下。"},
|
||
{ID: "357", Name: "CameraPreview", ZhName: "摄像头预览", Category: mediaCategory, Demo: cameraDemo, Code: cameraCode,
|
||
Keys: "Tab 聚焦预览;M 切换镜像。"},
|
||
{ID: "358", Name: "MicLevelMeter", ZhName: "麦克风电平", Category: mediaCategory, Demo: micDemo, Code: micCode,
|
||
Keys: "不获取焦点;读数随描述朗读。"},
|
||
{ID: "359", Name: "DeviceSelector", ZhName: "设备选择", Category: mediaCategory, Demo: deviceDemo, Code: deviceCode,
|
||
Keys: "Tab 在选择框间移动;Enter 打开;输入字母跳转。"},
|
||
{ID: "360", Name: "ImageCompare", ZhName: "前后对比", Category: mediaCategory, Demo: compareDemo, Code: compareCode,
|
||
Keys: "← / → 移动 1%;Home / End;可拖动。"},
|
||
}
|
||
|
||
// mediaSection titles a part of a demo.
|
||
func mediaSection(c *ui.Context, title string, fn func()) {
|
||
ui.Column(c).Gap(8).FillWidth().Children(func() {
|
||
ui.Text(c, title).FontSize(mujica.FontSize(c, theme.CaptionSize)).TextColor(mujica.Tokens(c).TextMuted)
|
||
fn()
|
||
})
|
||
}
|
||
|
||
// mediaCheck is a parameter checkbox that logs its changes.
|
||
func mediaCheck(c *ui.Context, log func(string), v *bool, label string) {
|
||
before := *v
|
||
mujica.Checkbox(c, v, label, mujica.CheckboxOptions{})
|
||
if *v != before {
|
||
log(fmt.Sprintf("%s = %v", label, *v))
|
||
}
|
||
}
|
||
|
||
// mediaPicture draws a w×h court scene in variant hues.
|
||
func mediaPicture(w, h, variant int) *image.RGBA {
|
||
walls := []color.RGBA{{36, 28, 32, 255}, {242, 235, 221, 255}, {73, 42, 54, 255}}
|
||
arch := []color.RGBA{{125, 32, 52, 255}, {173, 145, 100, 255}, {53, 94, 128, 255}}
|
||
img := image.NewRGBA(image.Rect(0, 0, w, h))
|
||
cx := float64(w) / 2
|
||
for y := range h {
|
||
for x := range w {
|
||
c := walls[variant%3]
|
||
dx, dy := (float64(x)-cx)/(float64(w)*0.28), (float64(y)-float64(h)*0.55)/(float64(h)*0.5)
|
||
if dy > 0 && math.Abs(dx) < 1 || dx*dx+dy*dy < 1 {
|
||
c = arch[variant%3]
|
||
}
|
||
if x%(w/6) < w/40 || y > h*7/8 {
|
||
c = color.RGBA{189, 161, 114, 255}
|
||
}
|
||
if math.Abs(float64(x)-cx)/float64(w)+math.Abs(float64(y)-float64(h)*0.45)/float64(h) < 0.06 {
|
||
c = color.RGBA{251, 247, 239, 255}
|
||
}
|
||
img.Set(x, y, c)
|
||
}
|
||
}
|
||
return img
|
||
}
|
||
|
||
var (
|
||
mediaPhotos = []*image.RGBA{mediaPicture(480, 270, 0), mediaPicture(480, 270, 1), mediaPicture(360, 360, 2)}
|
||
mediaBitmaps = func() []*ui.Bitmap {
|
||
out := make([]*ui.Bitmap, len(mediaPhotos))
|
||
for i, p := range mediaPhotos {
|
||
out[i] = ui.NewBitmap(p)
|
||
}
|
||
return out
|
||
}()
|
||
)
|
||
|
||
// mediaClockLabel writes d as m:ss for event logs.
|
||
func mediaClockLabel(d time.Duration) string {
|
||
s := int(d / time.Second)
|
||
return fmt.Sprintf("%d:%02d", s/60, s%60)
|
||
}
|