diff options
Diffstat (limited to 'src/FbWinFrame.cc')
-rw-r--r-- | src/FbWinFrame.cc | 253 |
1 files changed, 121 insertions, 132 deletions
diff --git a/src/FbWinFrame.cc b/src/FbWinFrame.cc index 4734b2b..d044dd7 100644 --- a/src/FbWinFrame.cc +++ b/src/FbWinFrame.cc | |||
@@ -1459,138 +1459,6 @@ void FbWinFrame::applyDecorations() { | |||
1459 | frameExtentSig().notify(); | 1459 | frameExtentSig().notify(); |
1460 | } | 1460 | } |
1461 | 1461 | ||
1462 | /* For aspect ratios | ||
1463 | Note that its slightly simplified in that only the | ||
1464 | line gradient is given - this is because for aspect | ||
1465 | ratios, we always have the line going through the origin | ||
1466 | |||
1467 | * Based on this formula: | ||
1468 | http://astronomy.swin.edu.au/~pbourke/geometry/pointline/ | ||
1469 | |||
1470 | Note that a gradient from origin goes through ( grad , 1 ) | ||
1471 | */ | ||
1472 | |||
1473 | void closestPointToLine(double &ret_x, double &ret_y, | ||
1474 | double point_x, double point_y, | ||
1475 | double gradient) { | ||
1476 | double u = (point_x * gradient + point_y) / | ||
1477 | (gradient*gradient + 1); | ||
1478 | |||
1479 | ret_x = u*gradient; | ||
1480 | ret_y = u; | ||
1481 | } | ||
1482 | |||
1483 | /** | ||
1484 | * Changes width and height to the nearest (lower) value | ||
1485 | * that conforms to it's size hints. | ||
1486 | * | ||
1487 | * display_* give the values that would be displayed | ||
1488 | * to the user when resizing. | ||
1489 | * We use pointers for display_* since they are optional. | ||
1490 | * | ||
1491 | * See ICCCM section 4.1.2.3 | ||
1492 | */ | ||
1493 | void FbWinFrame::applySizeHints(int &width, int &height, | ||
1494 | int *display_width, int *display_height, | ||
1495 | bool maximizing) { | ||
1496 | |||
1497 | int i = width, j = height; | ||
1498 | |||
1499 | // Check minimum size | ||
1500 | if (width < 0 || width < static_cast<signed>(m_size_hints.min_width)) | ||
1501 | width = m_size_hints.min_width; | ||
1502 | |||
1503 | if (height < 0 || height < static_cast<signed>(m_size_hints.min_height)) | ||
1504 | height = m_size_hints.min_height; | ||
1505 | |||
1506 | // Check maximum size | ||
1507 | if (m_size_hints.max_width > 0 && width > static_cast<signed>(m_size_hints.max_width)) | ||
1508 | width = m_size_hints.max_width; | ||
1509 | |||
1510 | if (m_size_hints.max_height > 0 && height > static_cast<signed>(m_size_hints.max_height)) | ||
1511 | height = m_size_hints.max_height; | ||
1512 | |||
1513 | // we apply aspect ratios before incrementals | ||
1514 | // Too difficult to exactly satisfy both incremental+aspect | ||
1515 | // in most situations | ||
1516 | // (they really shouldn't happen at the same time anyway). | ||
1517 | |||
1518 | /* aspect ratios are applied exclusive to the m_size_hints.base_width | ||
1519 | * | ||
1520 | * m_size_hints.min_aspect_x width m_size_hints.max_aspect_x | ||
1521 | * ------------ < ------- < ------------ | ||
1522 | * m_size_hints.min_aspect_y height m_size_hints.max_aspect_y | ||
1523 | * | ||
1524 | * beware of integer maximum (so I'll use doubles instead and divide) | ||
1525 | * | ||
1526 | * The trick is how to get back to the aspect ratio with minimal | ||
1527 | * change - do we modify x, y or both? | ||
1528 | * A: we minimise the distance between the current point, and | ||
1529 | * the target aspect ratio (consider them as x,y coordinates) | ||
1530 | * Consider that the aspect ratio is a line, and the current | ||
1531 | * w/h is a point, so we're just using the formula for | ||
1532 | * shortest distance from a point to a line! | ||
1533 | * | ||
1534 | * When maximizing, we must not increase any of the sizes, because we | ||
1535 | * would end up with the window partly off a screen, so a simpler formula | ||
1536 | * is used in that case. | ||
1537 | */ | ||
1538 | |||
1539 | if (m_size_hints.min_aspect_y > 0 && m_size_hints.max_aspect_y > 0 && | ||
1540 | (height - m_size_hints.base_height) > 0) { | ||
1541 | double widthd = static_cast<double>(width - m_size_hints.base_width); | ||
1542 | double heightd = static_cast<double>(height - m_size_hints.base_height); | ||
1543 | |||
1544 | double min = static_cast<double>(m_size_hints.min_aspect_x) / | ||
1545 | static_cast<double>(m_size_hints.min_aspect_y); | ||
1546 | |||
1547 | double max = static_cast<double>(m_size_hints.max_aspect_x) / | ||
1548 | static_cast<double>(m_size_hints.max_aspect_y); | ||
1549 | |||
1550 | double actual = widthd / heightd; | ||
1551 | |||
1552 | if (max > 0 && min > 0 && actual > 0) { // don't even try otherwise | ||
1553 | bool changed = false; | ||
1554 | if (actual < min) { | ||
1555 | changed = true; | ||
1556 | if (maximizing) | ||
1557 | heightd = widthd / min; | ||
1558 | else | ||
1559 | closestPointToLine(widthd, heightd, widthd, heightd, min); | ||
1560 | } else if (actual > max) { | ||
1561 | changed = true; | ||
1562 | if (maximizing) | ||
1563 | widthd = heightd * max; | ||
1564 | else | ||
1565 | closestPointToLine(widthd, heightd, widthd, heightd, max); | ||
1566 | } | ||
1567 | |||
1568 | if (changed) { | ||
1569 | width = static_cast<int>(widthd) + m_size_hints.base_width; | ||
1570 | height = static_cast<int>(heightd) + m_size_hints.base_height; | ||
1571 | } | ||
1572 | } | ||
1573 | } | ||
1574 | |||
1575 | // enforce incremental size limits, wrt base size | ||
1576 | // only calculate this if we really need to | ||
1577 | i = (width - static_cast<signed>(m_size_hints.base_width)) / | ||
1578 | static_cast<signed>(m_size_hints.width_inc); | ||
1579 | width = i*static_cast<signed>(m_size_hints.width_inc) + | ||
1580 | static_cast<signed>(m_size_hints.base_width); | ||
1581 | |||
1582 | j = (height - static_cast<signed>(m_size_hints.base_height)) / | ||
1583 | static_cast<signed>(m_size_hints.height_inc); | ||
1584 | height = j*static_cast<signed>(m_size_hints.height_inc) + | ||
1585 | static_cast<signed>(m_size_hints.base_height); | ||
1586 | |||
1587 | if (display_width) | ||
1588 | *display_width = i; | ||
1589 | |||
1590 | if (display_height) | ||
1591 | *display_height = j; | ||
1592 | } | ||
1593 | |||
1594 | bool FbWinFrame::setBorderWidth(bool do_move) { | 1462 | bool FbWinFrame::setBorderWidth(bool do_move) { |
1595 | unsigned int border_width = theme()->border().width(); | 1463 | unsigned int border_width = theme()->border().width(); |
1596 | unsigned int win_bw = m_decoration_mask & DECORM_BORDER ? border_width : 0; | 1464 | unsigned int win_bw = m_decoration_mask & DECORM_BORDER ? border_width : 0; |
@@ -1800,3 +1668,124 @@ int FbWinFrame::yOffset() const { | |||
1800 | return 0; | 1668 | return 0; |
1801 | } | 1669 | } |
1802 | 1670 | ||
1671 | /* For aspect ratios | ||
1672 | Note that its slightly simplified in that only the | ||
1673 | line gradient is given - this is because for aspect | ||
1674 | ratios, we always have the line going through the origin | ||
1675 | |||
1676 | * Based on this formula: | ||
1677 | http://astronomy.swin.edu.au/~pbourke/geometry/pointline/ | ||
1678 | |||
1679 | Note that a gradient from origin goes through ( grad , 1 ) | ||
1680 | */ | ||
1681 | |||
1682 | void closestPointToAspect(unsigned int &ret_x, unsigned int &ret_y, | ||
1683 | unsigned int point_x, unsigned int point_y, | ||
1684 | unsigned int aspect_x, unsigned int aspect_y) { | ||
1685 | double u = static_cast<double>(point_x * aspect_x + point_y * aspect_y) / | ||
1686 | static_cast<double>(aspect_x * aspect_x + aspect_y * aspect_y); | ||
1687 | |||
1688 | ret_x = static_cast<unsigned int>(u * aspect_x); | ||
1689 | ret_y = static_cast<unsigned int>(u * aspect_y); | ||
1690 | } | ||
1691 | |||
1692 | /** | ||
1693 | * Changes width and height to the nearest (lower) value | ||
1694 | * that conforms to it's size hints. | ||
1695 | * | ||
1696 | * display_* give the values that would be displayed | ||
1697 | * to the user when resizing. | ||
1698 | * We use pointers for display_* since they are optional. | ||
1699 | * | ||
1700 | * See ICCCM section 4.1.2.3 | ||
1701 | */ | ||
1702 | void FbWinFrame::SizeHints::apply(unsigned int &width, unsigned int &height, | ||
1703 | bool maximizing) const { | ||
1704 | |||
1705 | // we apply aspect ratios before incrementals | ||
1706 | // Too difficult to exactly satisfy both incremental+aspect | ||
1707 | // in most situations | ||
1708 | // (they really shouldn't happen at the same time anyway). | ||
1709 | |||
1710 | /* aspect ratios are applied exclusive to the base size | ||
1711 | * | ||
1712 | * min_aspect_x width max_aspect_x | ||
1713 | * ------------ < ------- < ------------ | ||
1714 | * min_aspect_y height max_aspect_y | ||
1715 | * | ||
1716 | * beware of integer maximum (so I'll use doubles instead and divide) | ||
1717 | * | ||
1718 | * The trick is how to get back to the aspect ratio with minimal | ||
1719 | * change - do we modify x, y or both? | ||
1720 | * A: we minimise the distance between the current point, and | ||
1721 | * the target aspect ratio (consider them as x,y coordinates) | ||
1722 | * Consider that the aspect ratio is a line, and the current | ||
1723 | * w/h is a point, so we're just using the formula for | ||
1724 | * shortest distance from a point to a line! | ||
1725 | * | ||
1726 | * When maximizing, we must not increase any of the sizes, because we | ||
1727 | * would end up with the window partly off a screen, so a simpler formula | ||
1728 | * is used in that case. | ||
1729 | */ | ||
1730 | |||
1731 | if (min_aspect_y > 0 && width*min_aspect_y < min_aspect_x*height) { | ||
1732 | if (maximizing) | ||
1733 | height = width * min_aspect_y / min_aspect_x; | ||
1734 | else | ||
1735 | closestPointToAspect(width, height, width, height, | ||
1736 | min_aspect_x, min_aspect_y); | ||
1737 | } else if (max_aspect_x > 0 && width*max_aspect_y > max_aspect_x*height) { | ||
1738 | if (maximizing) | ||
1739 | width = height * max_aspect_x / max_aspect_y; | ||
1740 | else | ||
1741 | closestPointToAspect(width, height, width, height, | ||
1742 | max_aspect_x, max_aspect_y); | ||
1743 | } | ||
1744 | |||
1745 | // Check minimum size | ||
1746 | if (width < min_width) | ||
1747 | width = min_width; | ||
1748 | |||
1749 | if (height < min_height) | ||
1750 | height = min_height; | ||
1751 | |||
1752 | // Check maximum size | ||
1753 | if (max_width > 0 && width > max_width) | ||
1754 | width = max_width; | ||
1755 | |||
1756 | if (max_height > 0 && height > max_height) | ||
1757 | height = max_height; | ||
1758 | |||
1759 | // enforce incremental size limits, wrt base size | ||
1760 | width -= (width - base_width) % width_inc; | ||
1761 | height -= (height - base_height) % height_inc; | ||
1762 | } | ||
1763 | |||
1764 | // check if the given width and height satisfy the size hints | ||
1765 | bool FbWinFrame::SizeHints::valid(unsigned int w, unsigned int h) const { | ||
1766 | if (w < min_width || h < min_height) | ||
1767 | return false; | ||
1768 | |||
1769 | if (w > max_width || h > max_height) | ||
1770 | return false; | ||
1771 | |||
1772 | if ((w - base_width) % width_inc != 0) | ||
1773 | return false; | ||
1774 | |||
1775 | if ((h - base_height) % height_inc != 0) | ||
1776 | return false; | ||
1777 | |||
1778 | if (min_aspect_x * h > w * min_aspect_y) | ||
1779 | return false; | ||
1780 | |||
1781 | if (max_aspect_x * h < w * max_aspect_y) | ||
1782 | return false; | ||
1783 | |||
1784 | return true; | ||
1785 | } | ||
1786 | |||
1787 | void FbWinFrame::SizeHints::displaySize(int &i, int &j, | ||
1788 | unsigned int width, unsigned int height) const { | ||
1789 | i = static_cast<signed>(width - base_width) / width_inc; | ||
1790 | j = static_cast<signed>(height - base_height) / height_inc; | ||
1791 | } | ||