// extract the top-left and bottom-right points of an xrect as a 4-tuple: x, y, x2, y2 func coords(rect xrect.Rect) (min_x, min_y, max_x, max_y int) { min_x = rect.X() max_x = min_x + rect.Width() min_y = rect.Y() max_y = min_y + rect.Height() return }
func (t *tree) place(geom xrect.Rect) bool { if t.child == nil || geom == nil { return false } x, y, w, h := geom.X(), geom.Y(), geom.Width(), geom.Height() if !t.child.ValidDims(w, h, 1, 1, w, h) { return false } t.child.MoveResize(x, y, w, h) return true }
// return the coordinate part for an edge of a rectangle // for the top edge, this is just rect.Y(), but for the right edge, it's // rect.X() + rect.Width() to get the x-offset of the right edge func EdgePos(rect xrect.Rect, dir wm.Direction) int { switch dir { case wm.Top: return rect.Y() case wm.Right: return rect.X() + rect.Width() case wm.Bottom: return rect.Y() + rect.Height() case wm.Left: return rect.X() } log.Panic("Bad direction in EdgePosition") return 0 }
// parseDim takes a string and parses a width or height dimension from it. // The magic here is that while a string could just be a simple integer, // it could also be a float greater than 0 but <= 1 in terms of the current // head's geometry. func parseDim(geom xrect.Rect, gribbleDim gribble.Any, hght bool) (int, bool) { switch dim := gribbleDim.(type) { case int: return dim, true case float64: if dim <= 0 || dim > 1 { logger.Warning.Printf("'%s' not in the valid range (0, 1].", dim) return 0, false } if hght { return int(float64(geom.Height()) * dim), true } else { return int(float64(geom.Width()) * dim), true } } panic("unreachable") }
// parsePos takes a string and parses an x or y position from it. // The magic here is that while a string could just be a simple integer, // it could also be a float greater than 0 but <= 1 in terms of the current // head's geometry. func parsePos(geom xrect.Rect, gribblePos gribble.Any, y bool) (int, bool) { switch pos := gribblePos.(type) { case int: return pos, true case float64: if pos <= 0 || pos > 1 { logger.Warning.Printf("'%s' not in the valid range (0, 1].", pos) return 0, false } if y { return geom.Y() + int(float64(geom.Height())*pos), true } else { return geom.X() + int(float64(geom.Width())*pos), true } } panic("unreachable") }
func SideOfRectangle(geom xrect.Rect, x, y int) wm.Direction { // construct algebraic functions to delinate the rectangle into sections // around the center point like this: [X] // these are a little confusing because x11 addresses coordinates from the top-left, // where traditional euclidean graphs address from the bottom-left w, h := geom.Width(), geom.Height() slope := float64(h) / float64(w) bl_to_tr_y := int(-1.0*slope*float64(x)) + h tl_to_br_y := int(slope * float64(x)) var dir wm.Direction if x < w/2 { // left half of the rectangle switch { // we must be above both lines and on the left side of the midpoint case y <= tl_to_br_y: dir = wm.Top // we must be below both lines and on the left side of the midpoint case y >= bl_to_tr_y: dir = wm.Bottom // we are between the two lines and on the left side default: dir = wm.Left } } else { // right half of the rectangle if y <= bl_to_tr_y { // we must be above both lines and on the left side of the midpoint dir = wm.Top } else if y >= tl_to_br_y { // we must be below both lines and on the left side of the midpoint dir = wm.Bottom } else { // we are between the two lines and on the left side dir = wm.Right } } return dir }
func (inp *Input) Show(workarea xrect.Rect, label string, do func(inp *Input, text string), canceled func(inp *Input)) bool { if inp.showing { return false } inp.win.Stack(xproto.StackModeAbove) inp.input.Reset() text.DrawText(inp.label, inp.theme.Font, inp.theme.FontSize, inp.theme.FontColor, inp.theme.BgColor, label) pad, bs := inp.theme.Padding, inp.theme.BorderSize width := (pad * 2) + (bs * 2) + inp.label.Geom.Width() + inp.theme.InputWidth height := (pad * 2) + (bs * 2) + inp.label.Geom.Height() // position the damn window based on its width/height (i.e., center it) posx := workarea.X() + workarea.Width()/2 - width/2 posy := workarea.Y() + workarea.Height()/2 - height/2 inp.win.MoveResize(posx, posy, width, height) inp.label.Move(bs+pad, pad+bs) inp.bInp.MoveResize(pad+inp.label.Geom.X()+inp.label.Geom.Width(), 0, bs, height) inp.bTop.Resize(width, bs) inp.bBot.MoveResize(0, height-bs, width, bs) inp.bLft.Resize(bs, height) inp.bRht.MoveResize(width-bs, 0, bs, height) inp.input.Move(inp.bInp.Geom.X()+inp.bInp.Geom.Width(), bs) inp.showing = true inp.do = do inp.canceled = canceled inp.win.Map() inp.input.Focus() inp.historyIndex = len(inp.history) return true }
func (slct *Select) Show(workarea xrect.Rect, tabCompleteType int, groups []*SelectShowGroup, data interface{}) bool { if slct.showing { return false } // if there aren't any groups, we obviously don't need to show anything. if len(groups) == 0 { return false } // So maybe there are groups, but there aren't any items... foundItem := false for _, group := range groups { if len(group.items) > 0 { foundItem = true break } } if !foundItem { return false } slct.groups = groups slct.tabComplete = tabCompleteType slct.win.Stack(xproto.StackModeAbove) slct.input.Reset() // Position the initial list of items with no filter. slct.FilterItems("") // Create some short aliases and start computing the geometry of the // prompt window. bs := slct.theme.BorderSize pad := slct.theme.Padding inpHeight := slct.input.Geom.Height() height := 2*(bs+pad) + inpHeight + bs maxFontWidth := 0 didGroupSpacing := false for _, group := range slct.groups { if len(group.items) > 0 && group.hasGroup() { maxFontWidth = misc.Max(maxFontWidth, group.win.Geom.Width()) height += group.win.Geom.Height() + slct.theme.GroupSpacing didGroupSpacing = true } for _, item := range group.items { maxFontWidth = misc.Max(maxFontWidth, item.regular.Geom.Width()) height += item.regular.Geom.Height() + itemTopSpace + itemBotSpace } } if didGroupSpacing { height -= slct.theme.GroupSpacing } maxWidth := int(float64(workarea.Width()) * 0.8) width := misc.Min(maxWidth, maxFontWidth+2*(bs+pad)) // position the damn window based on its width/height (i.e., center it) posx := workarea.X() + workarea.Width()/2 - width/2 posy := workarea.Y() + workarea.Height()/2 - height/2 // Issue the configure requests. We also need to adjust the borders. slct.win.MoveResize(posx, posy, width, height) slct.bInp.MoveResize(0, bs+inpHeight, width, bs) slct.bTop.Resize(width, bs) slct.bBot.MoveResize(0, height-bs, width, bs) slct.bLft.Resize(bs, height) slct.bRht.MoveResize(width-bs, 0, bs, height) slct.showing = true slct.data = data slct.selected = -1 slct.win.Map() slct.bInp.Focus() return true }
// Show will map and show the slice of items provided. // // 'workarea' is the rectangle to position the prompt window in. (i.e., // typically the rectangle of the monitor to place it on.) // // 'keyStr' is an optional parameter. If this prompt is shown in // response to a keybinding, then keyStr should be the keybinding used. // If there are modifiers used in the keyStr, the prompt will automatically // close if all of the modifiers are released. (This is the "alt-tab" // functionality.) // Note that if you don't want this auto-closing feature, simply leave keyStr // blank, even if the prompt is shown in response to a key binding. // // Show returns false if the prompt cannot be shown for some reason. func (cycle *Cycle) Show(workarea xrect.Rect, keyStr string, items []*CycleItem) bool { if cycle.showing { return false } // If there are no items, obviously quit. if len(items) == 0 { return false } // Note that SmartGrab is smart and avoids races. Check it out // in xgbutil/keybind.go if you're interested. // This makes it impossible to press and release alt-tab too quickly // to have it not register. if cycle.config.Grab { if err := keybind.SmartGrab(cycle.X, cycle.X.Dummy()); err != nil { logger.Warning.Printf( "Could not grab keyboard for prompt cycle: %s", err) return false } } // Save the list of cycle items (this how we know when to cycle between // them). Namely, cycle.selected is an index to this list. cycle.items = items // Save the modifiers used, if any. cycle.grabMods, _, _ = keybind.ParseString(cycle.X, keyStr) // Put the prompt window on top of the window stack. cycle.win.Stack(xproto.StackModeAbove) // Create some short aliases and start computing the geometry of the // cycle window. bs := cycle.theme.BorderSize cbs := cycle.theme.IconBorderSize is := cycle.theme.IconSize pad := cycle.theme.Padding maxWidth := int(float64(workarea.Width()) * 0.8) x, y := bs+pad, bs+pad+cbs+cycle.fontHeight width := 2 * (bs + pad) height := (2 * (bs + pad + cbs)) + is + cbs + cycle.fontHeight maxFontWidth := 0 widthStatic := false // when true, we stop increasing width for _, item := range items { maxFontWidth = misc.Max(maxFontWidth, item.text.Geom.Width()) // Check if we should move on to the next row. if x+(is+(2*cbs))+pad+bs > maxWidth { x = bs + pad y += is + (2 * cbs) height += is + (2 * cbs) widthStatic = true } // Position the icon window and map its active version or its // inactive version if it's iconified. item.show(x, y) // Only increase the width if we're still adding icons to the first row. if !widthStatic { width += is + (2 * cbs) } x += is + (2 * cbs) } // If the computed width is less than the max font width, then increase // the width of the prompt to fit the longest window title. // Forcefully cap it as the maxWidth, though. if maxFontWidth+2*(pad+bs) > width { width = misc.Min(maxWidth, maxFontWidth+2*(pad+bs)) } // position the damn window based on its width/height (i.e., center it) posx := workarea.X() + workarea.Width()/2 - width/2 posy := workarea.Y() + workarea.Height()/2 - height/2 // Issue the configure requests. We also need to adjust the borders. cycle.win.MoveResize(posx, posy, width, height) cycle.bTop.Resize(width, bs) cycle.bBot.MoveResize(0, height-bs, width, bs) cycle.bLft.Resize(bs, height) cycle.bRht.MoveResize(width-bs, 0, bs, height) cycle.showing = true cycle.selected = -1 cycle.win.Map() return true }
func (msg *Message) Show(workarea xrect.Rect, message string, duration time.Duration, hidden func(msg *Message)) bool { if msg.showing { return false } msg.win.Stack(xproto.StackModeAbove) pad, bs := msg.theme.Padding, msg.theme.BorderSize height := pad + bs width := 0 for _, line := range strings.Split(strings.TrimSpace(message), "\n") { textWin := xwindow.Must(xwindow.Create(msg.X, msg.win.Id)) msg.textWins = append(msg.textWins, textWin) if len(line) == 0 { line = " " } textWin.Map() textWin.Move(bs+pad, height) text.DrawText(textWin, msg.theme.Font, msg.theme.FontSize, msg.theme.FontColor, msg.theme.BgColor, line) height += textWin.Geom.Height() if w := textWin.Geom.Width(); w > width { width = w } } height += pad + bs width += pad*2 + bs*2 // position the damn window based on its width/height (i.e., center it) posx := workarea.X() + workarea.Width()/2 - width/2 posy := workarea.Y() + workarea.Height()/2 - height/2 msg.win.MoveResize(posx, posy, width, height) msg.bTop.Resize(width, bs) msg.bBot.MoveResize(0, height-bs, width, bs) msg.bLft.Resize(bs, height) msg.bRht.MoveResize(width-bs, 0, bs, height) msg.showing = true msg.duration = duration msg.hidden = hidden msg.win.Map() msg.lastShow = time.Now() // If the duration is non-zero, then wait for that amount of time and // automatically hide the popup. Otherwise, focus the window and wait // for user interaction. if duration == 0 { msg.bTop.Focus() } else { go func() { // If `Hide` is called before the timeout expires, we'll // cancel the timeout. select { case <-time.After(duration): msg.Hide() case <-msg.cancelTimeout: } }() } return true }
func (v *Vertical) Place(geom xrect.Rect) { if geom == nil { return } msize, ssize := len(v.store.masters), len(v.store.slaves) mx, mw := geom.X(), int(float64(geom.Width())*v.proportion) sx, sw := mx+mw, geom.Width()-mw // If there are zero widths or they are too big, don't do anything. if mw <= 0 || mw > geom.Width() || sw <= 0 || sw > geom.Width() { return } if msize > 0 { mh := geom.Height() / msize if ssize == 0 { mw = geom.Width() } for i, item := range v.store.masters { item.client.FrameTile() item.client.MoveResize(false, mx, geom.Y()+i*mh, mw, mh) } } if ssize > 0 { if msize == 0 { sx, sw = geom.X(), geom.Width() } sy := geom.Y() for _, item := range v.store.slaves { sh := int(float64(geom.Height()) * item.proportion) item.client.FrameTile() item.client.MoveResize(false, sx, sy, sw, sh) sy += sh } } }