package display import ( "fmt" "log/slog" "net/url" "github.com/egoist/mygo/ui" "git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core" "git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/internal" "git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/theme" ) // LinkOptions configure a Link; empty URL means in-app action. type LinkOptions struct { // URL opens in the browser; paths go to the router. URL string // Error is the caller's open failure, shown below the link. Error string } // Link is underlined text; malformed URLs show an error instead. func Link(c *ui.Context, label string, opts LinkOptions) ui.Element { if label == "" { panic("mujica: Link needs a label") } k := core.Tokens(c) u := core.Density(c).Unit() external := false problem := opts.Error target := opts.URL if opts.URL != "" { ok := false external, ok = linkCheck(opts.URL) if !ok { external, target = false, "" if problem == "" { problem = fmt.Sprintf(core.Msg(c, "link.invalid"), opts.URL) } } } var l ui.Element ui.Column(c).Gap(u).AlignItems(ui.Start).Children(func() { ui.Row(c).Gap(u).AlignItems(ui.Center).Children(func() { l = ui.Link(c, label, target).Underline().DecorationThickness(1) internal.Ring(l, k.Focus, theme.ControlRadius) color := k.AccentText if l.Pressed() || l.Hovered() { color = k.Text } l.TextColor(color).DecorationColor(color) if external { Icon(c, "external-link", IconOptions{Size: 12, Tone: IconAccent}) } }) if problem != "" { l.Error(problem) ui.Text(c, problem).TextColor(k.Danger).FontSize(core.FontSize(c, theme.CaptionSize)) } }) if l.Clicked() { slog.Debug("mujica: link activate", "url", opts.URL, "opened", target != "") } return l } // linkCheck reports whether raw has a scheme and is openable. func linkCheck(raw string) (external, ok bool) { p, err := url.Parse(raw) if err != nil { return false, false } switch p.Scheme { case "": return false, p.Path != "" || p.RawQuery != "" case "http", "https": return true, p.Host != "" } return true, p.Opaque != "" || p.Host != "" || p.Path != "" }