#include "view_stack"
#include "view"
+#include "session"
#include <cstdio>
#include <cstring>
static Redraw_queue rdq;
+View const *
+View_stack::next_view(View const *v, View const *bg) const
+{
+ while (v)
+ {
+ v = _top.next(v);
+ if (!v)
+ return 0;
+
+ if (v->session() && v->session()->ignore())
+ continue;
+
+ if (!v->background())
+ return v;
+
+ if (v == _background || v == bg)
+ return v;
+ }
+
+ return 0;
+}
+
Rect
View_stack::outline(View const *v) const
{
if (_mode.flat() || !v->need_frame())
return *v;
else
- return v->grow(3);
+ return v->grow(v->frame_width());
}
void
Rect compound = old | outline(v);
-#if 0
/* update labels (except when moving the mouse cursor) */
if (v != top())
- _place_labels(compound);
-#endif
+ place_labels(compound);
/* update area on screen */
rdq.q(compound);
void
View_stack::draw_frame(View const *v) const
{
- if (_mode.flat() || !v->need_frame())
+ if (_mode.flat() || !v->need_frame() || !v->session())
return;
- Rgb32::Color color = v->focused() ? Rgb32::White : View::frame_color();
- _canvas->draw_rect(v->offset(-3, -3, 3, 3), Rgb32::Black);
- _canvas->draw_rect(v->offset(-2, -2, 2, 2), color);
- _canvas->draw_rect(v->offset(-1, -1, 1, 1), Rgb32::Black);
+ Rgb32::Color color = v->session()->color();
+ Rgb32::Color outline = v->focused() ? Rgb32::White : Rgb32::Black;
+
+ int w = v->frame_width()-1;
+ _canvas->draw_rect(v->offset(-1-w, -1-w, 1+w, 1+w), outline);
+ _canvas->draw_rect(*v, color, -w);
+}
+
+static void
+draw_string_outline(Canvas *c, Point const &pos, Font const *f, char const *txt)
+{
+ for (int i = -1; i <= 1; ++i)
+ for (int k = -1; k <= 1; ++k)
+ if (i || k)
+ c->draw_string(pos + Point(i, k), f, Rgb32::Black, txt);
}
void
{
if (_mode.flat() || !v->need_frame())
return;
+
+ char const *const sl = v->session()->label();
+ Point pos = v->label_pos() + Point(1, 1);
+ draw_string_outline(_canvas, pos, _label_font, sl);
+ _canvas->draw_string(pos, _label_font, Rgb32::White, sl);
+
+ char const *const vl = v->title();
+ if (!vl)
+ return;
+
+ pos = pos + Point(_label_font->str_w(sl) + View::Label_sep, 0);
+ draw_string_outline(_canvas, pos, _label_font, vl);
+ _canvas->draw_string(pos, _label_font, Rgb32::White, vl);
+}
+
+void
+View_stack::set_focused(View *v)
+{
+ _focused = v;
+ if (!v)
+ return;
+
+ // stack all 'above' tagged views of the focussed session
+ // to the top. We keep their respective order.
+ Session *s = v->session();
+ if (!s)
+ return;
+
+ View *top = _no_stay_top;
+ for (View *t = _top.first(); t; t = _top.next(t))
+ {
+ if (!t->above())
+ continue;
+
+ if (t->session() == s)
+ {
+ stack(t, top, true);
+ top = t;
+ }
+ }
+
+ // if the view is not a background than raise the view relative to
+ // all views of other sessions, but keep the stacking order within
+ // it's session
+ if (v->background())
+ return;
+
+ for (top = _top.prev(v); top != _no_stay_top && top && top->session() != s;
+ top = _top.prev(top))
+ ;
+
+ if (top != _top.prev(v))
+ stack(v, top, true);
}
void
View_stack::draw_recursive(View const *v, View const *dst, Rect const &rect) const
+{ draw_recursive(v, dst, rect, current_background()); }
+
+void
+View_stack::draw_recursive(View const *v, View const *dst, Rect const &rect,
+ View const *bg) const
{
Rect clipped;
/* find next view that intersects with the current clipping rectangle */
- for ( ; v && !(clipped = outline(v) & rect).valid(); )
- v = v->next();
+ while (v && !(clipped = outline(v) & rect).valid())
+ v = next_view(v, bg);
if (!v)
return;
- Rect_tuple border = rect - clipped;
+ View const *n = next_view(v, bg);
+ Rect_tuple border;
- View const *n = v->next();
- if (n && border.t.valid())
- draw_recursive(n, dst, border.t);
- if (n && border.l.valid())
- draw_recursive(n, dst, border.l);
+ if (v->transparent() && n)
+ {
+ draw_recursive(n, dst, rect, bg);
+ n = 0;
+ }
+ else
+ border = rect - clipped;
+
+ if (n && border.t().valid())
+ draw_recursive(n, dst, border.t(), bg);
+ if (n && border.l().valid())
+ draw_recursive(n, dst, border.l(), bg);
if (!dst || dst == v || v->transparent())
{
draw_label(v);
}
- if (n && border.r.valid())
- draw_recursive(n, dst, border.r);
- if (n && border.b.valid())
- draw_recursive(n, dst, border.b);
+ if (n && border.r().valid())
+ draw_recursive(n, dst, border.r(), bg);
+ if (n && border.b().valid())
+ draw_recursive(n, dst, border.b(), bg);
}
void
void
View_stack::flush()
{
+ //static int cnt = 0;
for (Redraw_queue::iterator i = rdq.begin(); i != rdq.end(); ++i)
{
+ //printf("redraw[%d] %d,%d-%d,%d\n", cnt++, i->x1(), i->y1(), i->x2(), i->y2());
draw_recursive(top(), 0, *i);
if (_canvas_view)
_canvas_view->refresh(i->x1(), i->y1(), i->w(), i->h());
else
insert_before(v, pivot);
+ place_labels(*v);
+
refresh_view(v, 0, outline(v));
}
+void
+View_stack::push_bottom(View *v)
+{
+ Session *s = v->session();
+ View *b = s ? s->background() : 0;
+ stack(v, (b && (b != v)) ? b : _background, false);
+}
+
View *
View_stack::find(Point const &pos) const
{
- View *n = top()->next();
+ View *bg = current_background();
+ View *n = next_view(top(), bg);
while (n && !n->contains(pos))
- n = n->next();
+ n = next_view(n, bg);
return n;
}
+void
+View_stack::optimize_label_rec(View *cv, View *lv, Rect const &rect, Rect *optimal,
+ View *bg) const
+{
+ /* if label already fits in optimized rectangle, we are happy */
+ if (optimal->fits(lv->label_sz()))
+ return;
+
+ /* find next view that intersects with the rectangle or the target view */
+ Rect clipped;
+ while (cv && cv != lv && !(clipped = outline(cv) & rect).valid())
+ cv = next_view(cv, bg);
+
+ /* reached end of view stack */
+ if (!cv)
+ return;
+
+ if (cv != lv && next_view(cv, bg))
+ {
+ /* cut current view from rectangle and go into sub rectangles */
+ Rect r[4] =
+ { Rect(rect.p1(), Point(rect.x2(), clipped.y1() - 1)),
+ Rect(rect.p1(), Point(clipped.x1() - 1, rect.y2())),
+ Rect(Point(clipped.x2() + 1, rect.y1()), rect.p2()),
+ Rect(Point(rect.x1(), clipped.y2() + 1), rect.p2()) };
+ for (int i = 0; i < 4; i++)
+ optimize_label_rec(next_view(cv, bg), lv, r[i], optimal, bg);
+
+ return;
+ }
+
+ /*
+ * Now, cv equals lv and we must decide how to configure the
+ * optimal rectangle.
+ */
+
+ /* stop if label does not fit vertically */
+ if (rect.h() < lv->label_sz().h())
+ return;
+
+ /*
+ * If label fits completely within current rectangle, we are done.
+ * If label's width is not fully visible, choose the widest rectangle.
+ */
+ if (rect.fits(lv->label_sz()) || (rect.w() > optimal->w()))
+ *optimal = rect;
+}
+
+void
+View_stack::do_place_labels(Rect const &rect) const
+{
+ View *bg = current_background();
+ View *start = next_view(_top.first(), bg);
+ /* ignore mouse cursor */
+ for (View *view = start; view && next_view(view); view = next_view(view, bg))
+ if ((*view & rect).valid())
+ {
+ Rect old(view->label_pos(), view->label_sz());
+
+ /* calculate best visible label position */
+ Rect rect = Rect(Point(0, 0), _canvas->size()) & *view;
+ Rect best;
+ optimize_label_rec(start, view, rect, &best, bg);
+
+ /*
+ * If label is not fully visible, we ensure to display the first
+ * (most important) part. Otherwise, we center the label horizontally.
+ */
+ int x = best.x1();
+ if (best.fits(view->label_sz()))
+ x += (best.w() - view->label_sz().w()) / 2;
+
+ view->label_pos(Point(x, best.y1()));
+
+ /* refresh old and new label positions */
+ refresh_view(view, view, old);
+ Rect n = Rect(view->label_pos(), view->label_sz());
+ refresh_view(view, view, n);
+ }
+}
+
}