// NewInput constructs Input values. It needs an X connection, a parent window, // the width of the input box, and theme information related for the font // and background. Padding separating the text and the edges of the window // may also be specified. // // While NewInput returns an *Input, a Input value also has an xwindow.Window // value embedded into it. Thus, an Input can also be treated as a normal // window on which you can assign callbacks, close, destroy, etc. // // As with all windows, the Input window should be destroyed when it is no // longer in used. func NewInput(X *xgbutil.XUtil, parent xproto.Window, width int, padding int, font *truetype.Font, fontSize float64, fontColor, bgColor render.Color) *Input { _, height := xgraphics.TextMaxExtents(font, fontSize, "M") height += misc.TextBreathe width, height = width+2*padding, height+2*padding img := xgraphics.New(X, image.Rect(0, 0, width, height)) win := xwindow.Must(xwindow.Create(X, parent)) win.Listen(xproto.EventMaskKeyPress) win.Resize(width, height) ti := &Input{ Window: win, img: img, Text: make([]rune, 0, 50), font: font, fontSize: fontSize, fontColor: fontColor, bgColor: bgColor, padding: padding, } ti.Render() return ti }
// DrawText is a convenience function that will create a new image, render // the provided text to it, paint the image to the provided window, and resize // the window to fit the text snugly. // // An error can occur when rendering the text to an image. func DrawText(win *xwindow.Window, font *truetype.Font, size float64, fontClr, bgClr color.RGBA, text string) error { // Over estimate the extents. ew, eh := xgraphics.TextMaxExtents(font, size, text) eh += misc.TextBreathe // <-- this is the bug // Create an image using the over estimated extents. img := xgraphics.New(win.X, image.Rect(0, 0, ew, eh)) xgraphics.BlendBgColor(img, bgClr) // Now draw the text, grab the (x, y) position advanced by the text, and // check for an error in rendering. x, y, err := img.Text(0, 0, fontClr, size, font, text) if err != nil { return err } // Resize the window to the geometry determined by (x, y). w, h := x, y+misc.TextBreathe // <-- also part of the bug win.Resize(w, h) // Now draw the image to the window and destroy it. img.XSurfaceSet(win.Id) subimg := img.SubImage(image.Rect(0, 0, w, h)) subimg.XDraw() subimg.XPaint(win.Id) img.Destroy() return nil }
// NewCycle creates a new prompt. As many prompts as you want can be created, // and they could even technically be shown simultaneously so long as at most // one of them is using a grab. (The grab will fail for the others and they // will not be shown.) // // CycleTheme and CycleConfig values can either use DefaultCycle{Theme,Config} // values found in this package, or custom ones can be created using // composite literals. func NewCycle(X *xgbutil.XUtil, theme *CycleTheme, config CycleConfig) *Cycle { cycle := &Cycle{ X: X, theme: theme, config: config, showing: false, selected: -1, grabMods: 0, } // Create all windows used for the base of the cycle prompt. // This obviously doesn't include the windows representing the items. cwin := func(p xproto.Window) *xwindow.Window { return xwindow.Must(xwindow.Create(X, p)) } cycle.win = cwin(X.RootWin()) cycle.bTop, cycle.bBot = cwin(cycle.win.Id), cwin(cycle.win.Id) cycle.bLft, cycle.bRht = cwin(cycle.win.Id), cwin(cycle.win.Id) // Make the top-level window override redirect so the window manager // doesn't mess with us. cycle.win.Change(xproto.CwOverrideRedirect, 1) // Set the colors of each window. cclr := func(w *xwindow.Window, clr render.Color) { w.Change(xproto.CwBackPixel, uint32(clr.Int())) } cclr(cycle.win, cycle.theme.BgColor) cclr(cycle.bTop, cycle.theme.BorderColor) cclr(cycle.bBot, cycle.theme.BorderColor) cclr(cycle.bLft, cycle.theme.BorderColor) cclr(cycle.bRht, cycle.theme.BorderColor) // Map the sub-windows once. (Real mapping only happens when // cycle.win is mapped.) cycle.bTop.Map() cycle.bBot.Map() cycle.bLft.Map() cycle.bRht.Map() // Connect the key response handler (i.e., the alt-tab'ing, canceling, etc.) cycle.keyResponse().Connect(X, X.Dummy()) // Guess the maximum font height. _, cycle.fontHeight = xgraphics.TextMaxExtents( cycle.theme.Font, cycle.theme.FontSize, "A") cycle.fontHeight += misc.TextBreathe return cycle }
func (f *Full) UpdateTitle() { if f == nil { return } title := f.client.Name() font := f.theme.Font fontSize := f.theme.FontSize aFontColor := f.theme.AFontColor.ImageColor() iFontColor := f.theme.IFontColor.ImageColor() ew, eh := xgraphics.TextMaxExtents(font, fontSize, title) eh += misc.TextBreathe imgA := render.NewBorder(f.X, 0, render.NoColor, f.theme.ATitleColor, ew, f.theme.TitleSize, render.GradientVert, render.GradientRegular) imgI := render.NewBorder(f.X, 0, render.NoColor, f.theme.ITitleColor, ew, f.theme.TitleSize, render.GradientVert, render.GradientRegular) y := (f.theme.TitleSize-eh)/2 - 1 x2, _, err := imgA.Text(0, y, aFontColor, fontSize, font, title) if err != nil { logger.Warning.Printf("Could not draw window title for window %s "+ "because: %v", f.client, err) } _, _, err = imgI.Text(0, y, iFontColor, fontSize, font, title) if err != nil { logger.Warning.Printf("Could not draw window title for window %s "+ "because: %v", f.client, err) } f.titleText.Create( imgA.SubImage(image.Rect(0, 0, x2, imgA.Bounds().Max.Y)), imgI.SubImage(image.Rect(0, 0, x2, imgI.Bounds().Max.Y))) f.titleText.MROpt(fW, 0, 0, x2, 0) if f.client.State() == Active { f.titleText.Active() } else { f.titleText.Inactive() } }