/* PIP - Platform Independent Primitives Basic PIScreen tile Copyright (C) 2018 Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "piscreentile.h" #include "piscreendrawer.h" using namespace PIScreenTypes; PIScreenTile::PIScreenTile(const PIString & n, Direction d, SizePolicy p): PIObject(n) { direction = d; size_policy = p; focus_flags = 0; screen = 0; minimumWidth = minimumHeight = x_ = y_ = width_ = height_ = pw = ph = 0; maximumWidth = maximumHeight = 65535; marginLeft = marginRight = marginTop = marginBottom = spacing = 0; parent = 0; back_symbol = ' '; visible = true; has_focus = false; } PIScreenTile::~PIScreenTile() { //piCout << this << "~"; if (screen) screen->tileRemovedInternal(this); setScreen(0); deleteChildren(); if (!parent) return; parent->tiles.removeOne(this); } void PIScreenTile::addTile(PIScreenTile * t) { if (t == 0) return; if (tiles.contains(t)) return; tiles << t; t->parent = this; t->setScreen(screen); } void PIScreenTile::takeTile(PIScreenTile * t) { if (!tiles.contains(t)) return; tiles.removeOne(t); t->parent = 0; t->setScreen(0); } void PIScreenTile::removeTile(PIScreenTile * t) { if (!tiles.contains(t)) return; tiles.removeOne(t); t->parent = 0; t->setScreen(0); delete t; } PIVector PIScreenTile::children(bool only_visible) { PIVector ret; piForeach (PIScreenTile * t, tiles) if (t->visible || !only_visible) ret << t << t->children(only_visible); return ret; } PIScreenTile * PIScreenTile::childUnderMouse(int x, int y) { piForeach (PIScreenTile * t, tiles) { if (!t->visible) continue; if (x >= t->x_ && (x - t->x_) < t->width_ && y >= t->y_ && (y - t->y_) < t->height_) { return t; } } return 0; } void PIScreenTile::raiseEvent(TileEvent e) { if (!screen) return; screen->tileEventInternal(this, e); } void PIScreenTile::setScreen(PIScreenBase * s) { screen = s; piForeach (PIScreenTile * t, tiles) t->setScreen(s); } void PIScreenTile::deleteChildren() { //piCout << this << "deleteChildren"; piForeach (PIScreenTile * t, tiles) { //piCout << this << " child" << t; //t->deleteChildren(); t->parent = 0; delete t; } tiles.clear(); } void PIScreenTile::setFocus() { if (!screen || !focus_flags[CanHasFocus]) return; screen->tileSetFocusInternal(this); } void PIScreenTile::drawEventInternal(PIScreenDrawer * d) { if (!visible) { //d->clearRect(x, y, x + width, y + height); return; } d->fillRect(x_, y_, x_ + width_, y_ + height_, back_symbol, (Color)back_format.color_char, (Color)back_format.color_back, back_format.flags); drawEvent(d); piForeach (PIScreenTile * t, tiles) t->drawEventInternal(d); } void PIScreenTile::sizeHint(int & w, int & h) const { w = 0; h = 0; if (tiles.isEmpty()) return; int sl = spacing * (tiles.size_s() - 1); if (direction == Horizontal) w += sl; else h += sl; piForeachC (PIScreenTile * t, tiles) { if (!t->visible) continue; int cw(0), ch(0); t->sizeHint(cw, ch); cw = piClampi(cw, t->minimumWidth, t->maximumWidth); ch = piClampi(ch, t->minimumHeight, t->maximumHeight); if (direction == Horizontal) { w += cw; h = piMaxi(h, ch); } else { h += ch; w = piMaxi(w, cw); } } w += marginLeft + marginRight; h += marginTop + marginBottom; } void PIScreenTile::layout() { if (tiles.isEmpty() || !visible) return; int as(0), ts(0), ts2(0), ecnt(0), pcnt(0); ts = (direction == Horizontal) ? (width_ - marginLeft - marginRight) : (height_ - marginTop - marginBottom); ts2 = (direction != Horizontal) ? (width_ - marginLeft - marginRight) : (height_ - marginTop - marginBottom); ts -= spacing * (tiles.size_s() - 1); PIVector hints(tiles.size_s()); PIVector asizes(tiles.size_s()); for (int i = 0; i < tiles.size_s(); ++i) { PIScreenTile * t(tiles[i]); int cw(0), ch(0), cs(0); if (t->visible && t->needLayout()) { t->sizeHint(cw, ch); cw = piClampi(cw, t->minimumWidth, t->maximumWidth); ch = piClampi(ch, t->minimumHeight, t->maximumHeight); if (t->size_policy == Expanding) ++ecnt; if (t->size_policy == Preferred) ++pcnt; cs = (direction == Horizontal) ? cw : ch; as += cs; } hints[i] = cs; asizes[i] = 0.f; } if (as <= ts) { int acnt(0); SizePolicy pol = Fixed; if (ecnt > 0) { acnt = ecnt; pol = Expanding; } else if (pcnt > 0) { acnt = pcnt; pol = Preferred; } if (acnt > 0) { float add_a = float(ts - as), add_s = add_a / acnt, add_da(0.); asizes.fill(add_s); PISet max_tl; for (int i = 0; i < tiles.size_s(); ++i) { if (tiles[i]->size_policy == pol && tiles[i]->visible && tiles[i]->needLayout()) { float maxs = (direction == Horizontal) ? tiles[i]->maximumWidth : tiles[i]->maximumHeight; if (hints[i] + asizes[i] > maxs) { max_tl << i; float pas = asizes[i]; asizes[i] = maxs - hints[i]; acnt--; if (acnt > 0) { pas = (pas - asizes[i]) / acnt; for (int j = 0; j < tiles.size_s(); ++j) { if (i == j) continue; if (max_tl[j]) continue; if (tiles[j]->size_policy == pol && tiles[j]->visible && tiles[j]->needLayout()) asizes[j] += pas; } } } } } for (int i = 0; i < tiles.size_s(); ++i) { if (tiles[i]->size_policy == pol && tiles[i]->visible && tiles[i]->needLayout()) { int a = piRound(asizes[i] + add_da); add_da += asizes[i] - a; hints[i] += a; } } } } int cx = x_ + marginLeft, cy = y_ + marginTop; for (int i = 0; i < tiles.size_s(); ++i) { PIScreenTile * t(tiles[i]); if (!t->visible || !t->needLayout()) continue; t->x_ = cx; t->y_ = cy; if (direction == Horizontal) { t->width_ = hints[i]; t->height_ = ts2; cx += hints[i] + spacing; } else { t->width_ = ts2; t->height_ = hints[i]; cy += hints[i] + spacing; } if (t->pw != t->width_ || t->ph != t->height_) t->resizeEvent(t->width_, t->height_); t->pw = t->width_; t->ph = t->height_; t->layout(); } }