Compare commits

...

3 Commits

3 changed files with 96 additions and 46 deletions

View File

@ -15,8 +15,9 @@ type Application interface {
// Describe returns a description of the application. // Describe returns a description of the application.
Describe () ApplicationDescription Describe () ApplicationDescription
// Init performs the initial setup of the application. // Init performs the initial setup of the application. This behavior
Init () error // should return a window if it creates one.
Init () (tomo.Window, error)
} }
// ApplicationURLOpener is an application that can open a URL. // ApplicationURLOpener is an application that can open a URL.
@ -29,12 +30,15 @@ type ApplicationURLOpener interface {
// //
// Applications should support the file:// scheme at the very least, and // Applications should support the file:// scheme at the very least, and
// should also support others like http:// and https:// if possible. // should also support others like http:// and https:// if possible.
OpenURL (*url.URL) error //
// This behavior should return a window if it creates one.
OpenURL (*url.URL) (tomo.Window, error)
// OpenNone is called when the application is launched without any URLs // OpenNone is called when the application is launched without any URLs
// to open. The application may create some sort of default starting // to open. The application may create some sort of default starting
// window, or call tomo.Stop(). // window, or do nothing. This behavior should return a window if it
OpenNone () // creates one.
OpenNone () (tomo.Window, error)
} }
// ApplicationFlagAdder is an application that supports reading command line // ApplicationFlagAdder is an application that supports reading command line
@ -148,15 +152,19 @@ func RunApplication (application Application) {
if err != nil { log.Fatalln("nasin: could not set icon set:", err) } if err != nil { log.Fatalln("nasin: could not set icon set:", err) }
err = reg.SetFaceSet() err = reg.SetFaceSet()
if err != nil { log.Fatalln("nasin: could not set face set:", err) } if err != nil { log.Fatalln("nasin: could not set face set:", err) }
err = application.Init() window, err := application.Init()
if err != nil { log.Fatalln("nasin: could not run application:", err) } if err != nil { log.Fatalln("nasin: could not run application:", err) }
manageWindow(window)
// open URLs // open URLs
args := flag.Args() args := flag.Args()
applicationOpenUrls(application, args...) applicationOpenUrls(application, args...)
if windows > 0 {
err = backend.Run() err = backend.Run()
if err != nil { log.Fatalln("nasin: could not run application:", err) } if err != nil { log.Fatalln("nasin: could not run application:", err) }
} }
}
// NewApplicationWindow creates a window for an application. It will // NewApplicationWindow creates a window for an application. It will
// automatically set window information to signal to the OS that the window is // automatically set window information to signal to the OS that the window is
@ -173,10 +181,54 @@ func NewApplicationWindow (application Application, bounds image.Rectangle) (tom
return window, nil return window, nil
} }
func applicationOpenUrls (application Application, args ...string) { var windows int
if application, ok := application.(ApplicationURLOpener); ok {
func manageWindow (window tomo.Window) {
if window == nil { return }
windows ++
window.OnClose(func () {
windows --
if windows < 1 {
tomo.Stop()
}
})
}
func errorPopupf (title, format string, v ...any) func (func ()) {
return func (callback func ()) {
dialog, err := objects.NewDialogOk (
objects.DialogError, nil,
title,
fmt.Sprintf(format, v...),
callback)
if err != nil { log.Fatal(err) }
dialog.SetVisible(true)
manageWindow(dialog)
}
}
func applicationOpenUrls (app Application, args ...string) {
application, ok := app.(ApplicationURLOpener)
if !ok {
if len(args) > 0 {
log.Fatal("nasin: this application cannot open URLs")
}
return
}
openNone := func () bool {
window, err := application.OpenNone()
if err != nil {
log.Fatalf("nasin: could not open main window: %v", err)
return false
}
manageWindow(window)
return true
}
if len(args) <= 0 { if len(args) <= 0 {
application.OpenNone() openNone()
return
} }
openedAny := false openedAny := false
@ -190,27 +242,19 @@ func applicationOpenUrls (application Application, args ...string) {
if ur.Scheme == "" { if ur.Scheme == "" {
ur.Scheme = "file" ur.Scheme = "file"
} }
err = application.OpenURL(ur) window, err := application.OpenURL(ur)
if err != nil { if err != nil {
dialog, err := objects.NewDialogOk ( errorPopupf(
objects.DialogError, nil,
"Could Not Open URL", "Could Not Open URL",
fmt.Sprintf (
"Could not open %v: %v", "Could not open %v: %v",
arg, err), arg, err,
func () { )(func () {
if !openedAny { if !openedAny {
application.OpenNone() openNone()
} }
}) })
if err != nil { log.Fatal(err) }
dialog.SetVisible(true)
}
}
} else {
if len(args) > 0 {
log.Fatal("nasin: this application cannot open URLs")
} }
manageWindow(window)
} }
} }

View File

@ -61,7 +61,7 @@ func New () (*style.Style, event.Cookie) {
atlasTexture := tomo.NewTexture(atlasImage) atlasTexture := tomo.NewTexture(atlasImage)
textureCheckboxChecked := atlasTexture.SubTexture(image.Rect( 0, 0, 12, 11)) textureCheckboxChecked := atlasTexture.SubTexture(image.Rect( 0, 0, 12, 11))
textureCorkboard := atlasTexture.SubTexture(image.Rect(16, 0, 28, 12)) // textureCorkboard := atlasTexture.SubTexture(image.Rect(16, 0, 28, 12))
textureTearLine := atlasTexture.SubTexture(image.Rect(16, 12, 18, 13)) textureTearLine := atlasTexture.SubTexture(image.Rect(16, 12, 18, 13))
textureHandleVertical := atlasTexture.SubTexture(image.Rect(28, 0, 29, 2)) textureHandleVertical := atlasTexture.SubTexture(image.Rect(28, 0, 29, 2))
textureHandleHorizontal := atlasTexture.SubTexture(image.Rect(28, 0, 30, 1)) textureHandleHorizontal := atlasTexture.SubTexture(image.Rect(28, 0, 30, 1))
@ -160,8 +160,8 @@ rules := []style.Rule {
Width: tomo.I(1, 0, 0, 1), Width: tomo.I(1, 0, 0, 1),
Color: borderColorEngraved, Color: borderColorEngraved,
}), }),
tomo.AColor(nil), tomo.AColor(tomo.ColorSunken),
tomo.ATexture(textureCorkboard), // tomo.ATexture(textureCorkboard),
tomo.APadding(8), tomo.APadding(8),
), tomo.R("", "Container"), "sunken"), ), tomo.R("", "Container"), "sunken"),
@ -189,6 +189,11 @@ rules := []style.Rule {
tomo.AAlign(tomo.AlignMiddle, tomo.AlignMiddle), tomo.AAlign(tomo.AlignMiddle, tomo.AlignMiddle),
), tomo.R("", "Heading")), ), tomo.R("", "Heading")),
// *.Heading
style.Ru(style.AS (
tomo.APadding(4, 8),
), tomo.R("", "Heading"), "menu"),
// *.Separator // *.Separator
style.Ru(style.AS ( style.Ru(style.AS (
tomo.AttrBorder { tomo.AttrBorder {
@ -498,7 +503,7 @@ rules := []style.Rule {
Color: borderColorLifted, Color: borderColorLifted,
}, },
}, },
tomo.AttrPadding(tomo.I(4, 8, 4, 8)), tomo.AttrPadding(tomo.I(4, 8, 7, 8)),
tomo.AttrColor { Color: tomo.ColorBackground }, tomo.AttrColor { Color: tomo.ColorBackground },
), tomo.R("", "Tab"), "active"), ), tomo.R("", "Tab"), "active"),

View File

@ -133,8 +133,8 @@ func (this *parser) parseSelector () (Selector, error) {
// tags // tags
err = this.ExpectNext(LBracket) err = this.ExpectNext(LBracket)
if err == nil { if err == nil {
this.Next()
for { for {
this.Next()
err := this.Expect(Ident, String, RBracket) err := this.Expect(Ident, String, RBracket)
if err != nil { return Selector { }, err } if err != nil { return Selector { }, err }
if this.Is(RBracket) { break } if this.Is(RBracket) { break }
@ -142,6 +142,7 @@ func (this *parser) parseSelector () (Selector, error) {
selector.Tags = append(selector.Tags, this.Value()) selector.Tags = append(selector.Tags, this.Value())
err = this.ExpectNext(Comma, RBracket) err = this.ExpectNext(Comma, RBracket)
if err != nil { return Selector { }, err } if err != nil { return Selector { }, err }
if this.Is(RBracket) { break }
} }
this.Next() this.Next()
} }