pub struct GridTrack { /* private fields */ }bevy_ui only.Expand description
A GridTrack is a Row or Column of a CSS Grid. This struct specifies what size the track should be.
See below for the different “track sizing functions” you can specify.
Implementations§
Source§impl GridTrack
impl GridTrack
pub const DEFAULT: GridTrack
Sourcepub fn px<T>(value: f32) -> T
pub fn px<T>(value: f32) -> T
Create a grid track with a fixed pixel size
Examples found in repository?
1135 pub fn setup(mut commands: Commands) {
1136 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Grid)));
1137 // Top-level grid (app frame)
1138 commands.spawn((
1139 Node {
1140 display: Display::Grid,
1141 width: percent(100),
1142 height: percent(100),
1143 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
1144 grid_template_rows: vec![
1145 GridTrack::auto(),
1146 GridTrack::flex(1.0),
1147 GridTrack::px(40.),
1148 ],
1149 ..default()
1150 },
1151 BackgroundColor(Color::WHITE),
1152 DespawnOnExit(super::Scene::Grid),
1153 children![
1154 // Header
1155 (
1156 Node {
1157 display: Display::Grid,
1158 grid_column: GridPlacement::span(2),
1159 padding: UiRect::all(px(40)),
1160 ..default()
1161 },
1162 BackgroundColor(RED.into()),
1163 ),
1164 // Main content grid (auto placed in row 2, column 1)
1165 (
1166 Node {
1167 height: percent(100),
1168 aspect_ratio: Some(1.0),
1169 display: Display::Grid,
1170 grid_template_columns: RepeatedGridTrack::flex(3, 1.0),
1171 grid_template_rows: RepeatedGridTrack::flex(2, 1.0),
1172 row_gap: px(12),
1173 column_gap: px(12),
1174 ..default()
1175 },
1176 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
1177 children![
1178 (Node::default(), BackgroundColor(ORANGE.into())),
1179 (Node::default(), BackgroundColor(BISQUE.into())),
1180 (Node::default(), BackgroundColor(BLUE.into())),
1181 (Node::default(), BackgroundColor(CRIMSON.into())),
1182 (Node::default(), BackgroundColor(AQUA.into())),
1183 ]
1184 ),
1185 // Right side bar (auto placed in row 2, column 2)
1186 (Node::DEFAULT, BackgroundColor(BLACK.into())),
1187 ],
1188 ));
1189 }
1190}
1191
1192mod borders {
1193 use bevy::{color::palettes::css::*, prelude::*};
1194
1195 pub fn setup(mut commands: Commands) {
1196 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Borders)));
1197 let root = commands
1198 .spawn((
1199 Node {
1200 flex_wrap: FlexWrap::Wrap,
1201 ..default()
1202 },
1203 DespawnOnExit(super::Scene::Borders),
1204 ))
1205 .id();
1206
1207 // all the different combinations of border edges
1208 let borders = [
1209 UiRect::default(),
1210 UiRect::all(px(20)),
1211 UiRect::left(px(20)),
1212 UiRect::vertical(px(20)),
1213 UiRect {
1214 left: px(40),
1215 top: px(20),
1216 ..Default::default()
1217 },
1218 UiRect {
1219 right: px(20),
1220 bottom: px(30),
1221 ..Default::default()
1222 },
1223 UiRect {
1224 right: px(20),
1225 top: px(40),
1226 bottom: px(20),
1227 ..Default::default()
1228 },
1229 UiRect {
1230 left: px(20),
1231 top: px(20),
1232 bottom: px(20),
1233 ..Default::default()
1234 },
1235 UiRect {
1236 left: px(20),
1237 right: px(20),
1238 bottom: px(40),
1239 ..Default::default()
1240 },
1241 ];
1242
1243 let non_zero = |x, y| x != px(0) && y != px(0);
1244 let border_size = |x, y| if non_zero(x, y) { f32::MAX } else { 0. };
1245
1246 for border in borders {
1247 for rounded in [true, false] {
1248 let border_node = commands
1249 .spawn((
1250 Node {
1251 width: px(100),
1252 height: px(100),
1253 border,
1254 margin: UiRect::all(px(30)),
1255 align_items: AlignItems::Center,
1256 justify_content: JustifyContent::Center,
1257 border_radius: if rounded {
1258 BorderRadius::px(
1259 border_size(border.left, border.top),
1260 border_size(border.right, border.top),
1261 border_size(border.right, border.bottom),
1262 border_size(border.left, border.bottom),
1263 )
1264 } else {
1265 BorderRadius::ZERO
1266 },
1267 ..default()
1268 },
1269 BackgroundColor(MAROON.into()),
1270 BorderColor::all(RED),
1271 Outline {
1272 width: px(10),
1273 offset: px(10),
1274 color: Color::WHITE,
1275 },
1276 ))
1277 .id();
1278
1279 commands.entity(root).add_child(border_node);
1280 }
1281 }
1282 }
1283}
1284
1285mod elliptical_border_radius {
1286 use bevy::{color::palettes::css::*, prelude::*};
1287
1288 pub fn setup(mut commands: Commands, assets: Res<AssetServer>) {
1289 commands.spawn((
1290 Camera2d,
1291 DespawnOnExit(super::Scene::EllipticalBorderRadius),
1292 ));
1293 commands
1294 .spawn((
1295 Node {
1296 width: percent(100),
1297 height: percent(100),
1298 flex_wrap: FlexWrap::Wrap,
1299 column_gap: px(40),
1300 row_gap: px(40),
1301 margin: auto().all(),
1302 padding: px(30).all(),
1303 ..default()
1304 },
1305 BackgroundColor(DARK_GRAY.into()),
1306 DespawnOnExit(super::Scene::EllipticalBorderRadius),
1307 ))
1308 .with_children(|builder| {
1309 builder.spawn((
1310 Node {
1311 width: px(200),
1312 height: px(100),
1313 border: UiRect::all(px(8)),
1314 border_radius: BorderRadius::new(
1315 CornerRadius {
1316 x: px(90),
1317 y: px(24),
1318 },
1319 CornerRadius {
1320 x: px(18),
1321 y: px(70),
1322 },
1323 CornerRadius {
1324 x: px(110),
1325 y: px(32),
1326 },
1327 CornerRadius {
1328 x: px(28),
1329 y: px(58),
1330 },
1331 ),
1332 ..default()
1333 },
1334 BackgroundColor(ORANGE.into()),
1335 BackgroundGradient::from(LinearGradient {
1336 stops: vec![
1337 RED.into(),
1338 Color::BLACK.into(),
1339 BLUE.into(),
1340 WHEAT.into(),
1341 GREEN.into(),
1342 ],
1343 ..default()
1344 }),
1345 BorderColor::all(RED),
1346 Outline {
1347 width: px(4),
1348 offset: px(8),
1349 color: WHITE.into(),
1350 },
1351 BoxShadow::from(ShadowStyle {
1352 blur_radius: px(5),
1353 ..default()
1354 }),
1355 ));
1356
1357 builder.spawn((
1358 Node {
1359 width: px(150),
1360 height: px(150),
1361 border: UiRect {
1362 left: px(16),
1363 right: px(4),
1364 top: px(24),
1365 bottom: px(8),
1366 },
1367 border_radius: BorderRadius::new(
1368 CornerRadius {
1369 x: percent(65),
1370 y: percent(20),
1371 },
1372 CornerRadius {
1373 x: percent(20),
1374 y: percent(65),
1375 },
1376 CornerRadius {
1377 x: percent(65),
1378 y: percent(20),
1379 },
1380 CornerRadius {
1381 x: percent(20),
1382 y: percent(65),
1383 },
1384 ),
1385 ..default()
1386 },
1387 BackgroundColor(MEDIUM_SEA_GREEN.into()),
1388 BorderColor::all(DARK_GREEN),
1389 Outline {
1390 width: px(3),
1391 offset: px(10),
1392 color: LIME.into(),
1393 },
1394 BoxShadow::from(ShadowStyle {
1395 blur_radius: px(5),
1396 ..default()
1397 }),
1398 ));
1399
1400 builder.spawn((
1401 Node {
1402 width: px(210),
1403 height: px(75),
1404 border: UiRect::axes(px(12), px(4)),
1405 border_radius: BorderRadius::new(
1406 CornerRadius {
1407 x: px(140),
1408 y: px(18),
1409 },
1410 CornerRadius {
1411 x: px(140),
1412 y: px(18),
1413 },
1414 CornerRadius {
1415 x: px(42),
1416 y: px(54),
1417 },
1418 CornerRadius {
1419 x: px(42),
1420 y: px(54),
1421 },
1422 ),
1423 ..default()
1424 },
1425 BackgroundColor(DODGER_BLUE.into()),
1426 BorderColor::all(NAVY),
1427 Outline {
1428 width: px(5),
1429 offset: px(6),
1430 color: SKY_BLUE.into(),
1431 },
1432 BoxShadow::from(ShadowStyle {
1433 blur_radius: px(5),
1434 ..default()
1435 }),
1436 ));
1437 builder.spawn((
1438 Node {
1439 width: px(160),
1440 height: px(120),
1441 border: UiRect::axes(px(20), px(20)),
1442 border_radius: BorderRadius::new(
1443 CornerRadius {
1444 x: px(50),
1445 y: px(10),
1446 },
1447 CornerRadius {
1448 x: px(50),
1449 y: px(10),
1450 },
1451 CornerRadius {
1452 x: px(50),
1453 y: px(10),
1454 },
1455 CornerRadius {
1456 x: px(50),
1457 y: px(10),
1458 },
1459 ),
1460 ..default()
1461 },
1462 BackgroundColor(RED.into()),
1463 BorderColor::all(WHITE),
1464 Outline {
1465 width: px(3),
1466 offset: px(5),
1467 color: SKY_BLUE.into(),
1468 },
1469 BoxShadow::from(ShadowStyle {
1470 blur_radius: px(5),
1471 ..default()
1472 }),
1473 ));
1474
1475 builder.spawn((
1476 Node {
1477 width: px(160),
1478 height: px(120),
1479 border: UiRect::axes(px(20), px(20)),
1480 border_radius: BorderRadius::all(px(1000)),
1481 ..default()
1482 },
1483 BackgroundColor(RED.into()),
1484 BorderColor::all(WHITE),
1485 Outline {
1486 width: px(3),
1487 offset: px(5),
1488 color: SKY_BLUE.into(),
1489 },
1490 BoxShadow::from(ShadowStyle {
1491 blur_radius: px(5),
1492 ..default()
1493 }),
1494 ));
1495
1496 builder.spawn((
1497 Node {
1498 width: px(160),
1499 height: px(120),
1500 border: UiRect::axes(px(20), px(20)),
1501 border_radius: BorderRadius {
1502 top_left: CornerRadius {
1503 x: px(1000),
1504 y: auto(),
1505 },
1506 top_right: CornerRadius {
1507 x: auto(),
1508 y: px(1000),
1509 },
1510 bottom_right: CornerRadius::circular(px(1000)),
1511 bottom_left: CornerRadius::circular(px(1000)),
1512 },
1513 ..default()
1514 },
1515 BackgroundColor(RED.into()),
1516 BorderColor::all(WHITE),
1517 Outline {
1518 width: px(3),
1519 offset: px(5),
1520 color: SKY_BLUE.into(),
1521 },
1522 BoxShadow::from(ShadowStyle {
1523 blur_radius: px(5),
1524 ..default()
1525 }),
1526 ));
1527
1528 builder.spawn((
1529 Node {
1530 width: px(160),
1531 height: px(120),
1532 border: UiRect::axes(px(20), px(20)),
1533 border_radius: BorderRadius::new(
1534 CornerRadius {
1535 x: px(25),
1536 y: px(20),
1537 },
1538 CornerRadius {
1539 x: px(20),
1540 y: px(25),
1541 },
1542 CornerRadius {
1543 x: px(20),
1544 y: px(25),
1545 },
1546 CornerRadius {
1547 x: px(20),
1548 y: px(25),
1549 },
1550 ),
1551 ..default()
1552 },
1553 BackgroundColor(RED.into()),
1554 ImageNode::from(assets.load("branding/icon.png")),
1555 BorderColor::all(WHITE),
1556 Outline {
1557 width: px(3),
1558 offset: px(5),
1559 color: SKY_BLUE.into(),
1560 },
1561 BoxShadow::from(ShadowStyle {
1562 blur_radius: px(5),
1563 ..default()
1564 }),
1565 ));
1566
1567 builder.spawn((
1568 Node {
1569 width: px(160),
1570 height: px(120),
1571 border: UiRect::axes(px(10), px(10)),
1572 border_radius: BorderRadius::new(
1573 CornerRadius {
1574 x: px(40),
1575 y: px(30),
1576 },
1577 CornerRadius {
1578 x: px(40),
1579 y: px(30),
1580 },
1581 CornerRadius {
1582 x: px(40),
1583 y: px(30),
1584 },
1585 CornerRadius {
1586 x: px(40),
1587 y: px(30),
1588 },
1589 ),
1590 ..default()
1591 },
1592 BackgroundColor(RED.into()),
1593 BorderColor::all(WHITE),
1594 Outline {
1595 width: px(3),
1596 offset: px(5),
1597 color: SKY_BLUE.into(),
1598 },
1599 BoxShadow::from(ShadowStyle {
1600 blur_radius: px(5),
1601 ..default()
1602 }),
1603 ));
1604
1605 builder.spawn((
1606 Node {
1607 width: px(160),
1608 height: px(80),
1609 border: UiRect::axes(px(10), px(10)),
1610 border_radius: BorderRadius::new(
1611 CornerRadius {
1612 x: percent(50),
1613 y: percent(50),
1614 },
1615 CornerRadius {
1616 x: percent(50),
1617 y: percent(50),
1618 },
1619 CornerRadius {
1620 x: percent(50),
1621 y: percent(50),
1622 },
1623 CornerRadius {
1624 x: percent(50),
1625 y: percent(50),
1626 },
1627 ),
1628 ..default()
1629 },
1630 BackgroundColor(RED.into()),
1631 BorderColor::all(WHITE),
1632 ImageNode::from(assets.load("branding/icon.png")),
1633 Outline {
1634 width: px(3),
1635 offset: px(5),
1636 color: SKY_BLUE.into(),
1637 },
1638 BoxShadow::from(ShadowStyle {
1639 blur_radius: px(5),
1640 ..default()
1641 }),
1642 ));
1643
1644 builder.spawn((
1645 Node {
1646 width: px(80),
1647 height: px(160),
1648 border: UiRect::axes(px(10), px(10)),
1649 border_radius: BorderRadius::new(
1650 CornerRadius {
1651 x: percent(50),
1652 y: percent(50),
1653 },
1654 CornerRadius {
1655 x: percent(50),
1656 y: percent(50),
1657 },
1658 CornerRadius {
1659 x: percent(50),
1660 y: percent(50),
1661 },
1662 CornerRadius {
1663 x: percent(50),
1664 y: percent(50),
1665 },
1666 ),
1667 ..default()
1668 },
1669 BackgroundColor(RED.into()),
1670 BorderColor::all(WHITE),
1671 ImageNode::from(assets.load("branding/icon.png")),
1672 Outline {
1673 width: px(3),
1674 offset: px(5),
1675 color: SKY_BLUE.into(),
1676 },
1677 BoxShadow::from(ShadowStyle {
1678 blur_radius: px(5),
1679 ..default()
1680 }),
1681 ));
1682
1683 builder.spawn((
1684 Node {
1685 width: px(160),
1686 height: px(80),
1687 border: UiRect::axes(px(20), px(10)),
1688 border_radius: BorderRadius::new(
1689 CornerRadius {
1690 x: percent(50),
1691 y: percent(50),
1692 },
1693 CornerRadius {
1694 x: percent(50),
1695 y: percent(50),
1696 },
1697 CornerRadius {
1698 x: percent(50),
1699 y: percent(50),
1700 },
1701 CornerRadius {
1702 x: percent(50),
1703 y: percent(50),
1704 },
1705 ),
1706 ..default()
1707 },
1708 BackgroundColor(RED.into()),
1709 BorderColor::all(WHITE),
1710 ImageNode::from(assets.load("branding/icon.png")),
1711 Outline {
1712 width: px(3),
1713 offset: px(5),
1714 color: SKY_BLUE.into(),
1715 },
1716 BoxShadow::from(ShadowStyle {
1717 blur_radius: px(5),
1718 ..default()
1719 }),
1720 ));
1721
1722 builder.spawn((
1723 Node {
1724 width: px(80),
1725 height: px(160),
1726 border: UiRect::all(px(10)).with_right(px(25)),
1727 border_radius: BorderRadius::new(
1728 CornerRadius {
1729 x: percent(50),
1730 y: percent(50),
1731 },
1732 CornerRadius {
1733 x: percent(50),
1734 y: percent(50),
1735 },
1736 CornerRadius {
1737 x: percent(50),
1738 y: percent(50),
1739 },
1740 CornerRadius {
1741 x: percent(50),
1742 y: percent(50),
1743 },
1744 ),
1745 ..default()
1746 },
1747 BackgroundColor(RED.into()),
1748 BorderColor::all(WHITE),
1749 Outline {
1750 width: px(3),
1751 offset: px(5),
1752 color: SKY_BLUE.into(),
1753 },
1754 BoxShadow::from(ShadowStyle {
1755 blur_radius: px(5),
1756 ..default()
1757 }),
1758 ));
1759
1760 builder.spawn((
1761 Node {
1762 width: px(160),
1763 height: px(80),
1764 border: UiRect::all(px(5)),
1765 border_radius: BorderRadius::new(
1766 CornerRadius {
1767 x: percent(50),
1768 y: percent(50),
1769 },
1770 CornerRadius {
1771 x: percent(50),
1772 y: percent(50),
1773 },
1774 CornerRadius {
1775 x: percent(50),
1776 y: percent(50),
1777 },
1778 CornerRadius {
1779 x: px(20),
1780 y: px(20),
1781 },
1782 ),
1783 ..default()
1784 },
1785 BackgroundColor(RED.into()),
1786 BorderColor::all(WHITE),
1787 ImageNode::from(assets.load("branding/icon.png")),
1788 Outline {
1789 width: px(3),
1790 offset: px(5),
1791 color: SKY_BLUE.into(),
1792 },
1793 BoxShadow::from(ShadowStyle {
1794 blur_radius: px(5),
1795 ..default()
1796 }),
1797 ));
1798 });
1799 }
1800}
1801
1802mod box_shadow {
1803 use bevy::{color::palettes::css::*, prelude::*};
1804
1805 pub fn setup(mut commands: Commands) {
1806 commands.spawn((Camera2d, DespawnOnExit(super::Scene::BoxShadow)));
1807
1808 commands
1809 .spawn((
1810 Node {
1811 width: percent(100),
1812 height: percent(100),
1813 padding: UiRect::all(px(30)),
1814 column_gap: px(200),
1815 flex_wrap: FlexWrap::Wrap,
1816 ..default()
1817 },
1818 BackgroundColor(GREEN.into()),
1819 DespawnOnExit(super::Scene::BoxShadow),
1820 ))
1821 .with_children(|commands| {
1822 let example_nodes = [
1823 (
1824 Vec2::splat(100.),
1825 Vec2::ZERO,
1826 10.,
1827 0.,
1828 BorderRadius::bottom_right(px(10)),
1829 ),
1830 (Vec2::new(200., 50.), Vec2::ZERO, 10., 0., BorderRadius::MAX),
1831 (
1832 Vec2::new(100., 50.),
1833 Vec2::ZERO,
1834 10.,
1835 10.,
1836 BorderRadius::ZERO,
1837 ),
1838 (
1839 Vec2::splat(100.),
1840 Vec2::splat(20.),
1841 10.,
1842 10.,
1843 BorderRadius::bottom_right(px(10)),
1844 ),
1845 (
1846 Vec2::splat(100.),
1847 Vec2::splat(50.),
1848 0.,
1849 10.,
1850 BorderRadius::ZERO,
1851 ),
1852 (
1853 Vec2::new(50., 100.),
1854 Vec2::splat(10.),
1855 0.,
1856 10.,
1857 BorderRadius::MAX,
1858 ),
1859 ];
1860
1861 for (size, offset, spread, blur, border_radius) in example_nodes {
1862 commands.spawn((
1863 Node {
1864 width: px(size.x),
1865 height: px(size.y),
1866 border: UiRect::all(px(2)),
1867 border_radius,
1868 ..default()
1869 },
1870 BorderColor::all(WHITE),
1871 BackgroundColor(BLUE.into()),
1872 BoxShadow::new(
1873 Color::BLACK.with_alpha(0.9),
1874 percent(offset.x),
1875 percent(offset.y),
1876 percent(spread),
1877 px(blur),
1878 ),
1879 ));
1880 }
1881 });
1882 }
1883}
1884
1885mod text_wrap {
1886 use bevy::prelude::*;
1887
1888 pub fn setup(mut commands: Commands) {
1889 commands.spawn((Camera2d, DespawnOnExit(super::Scene::TextWrap)));
1890
1891 let root = commands
1892 .spawn((
1893 Node {
1894 flex_direction: FlexDirection::Column,
1895 width: px(200),
1896 height: percent(100),
1897 overflow: Overflow::clip_x(),
1898 ..default()
1899 },
1900 BackgroundColor(Color::BLACK),
1901 DespawnOnExit(super::Scene::TextWrap),
1902 ))
1903 .id();
1904
1905 for linebreak in [
1906 LineBreak::AnyCharacter,
1907 LineBreak::WordBoundary,
1908 LineBreak::WordOrCharacter,
1909 LineBreak::NoWrap,
1910 ] {
1911 let messages = [
1912 "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".to_string(),
1913 "pneumonoultramicroscopicsilicovolcanoconiosis".to_string(),
1914 ];
1915
1916 for (j, message) in messages.into_iter().enumerate() {
1917 commands.entity(root).with_child((
1918 Text(message.clone()),
1919 TextLayout::new(Justify::Left, linebreak),
1920 BackgroundColor(Color::srgb(0.8 - j as f32 * 0.3, 0., 0.)),
1921 ));
1922 }
1923 }
1924 }
1925}
1926
1927mod overflow {
1928 use bevy::{color::palettes::css::*, prelude::*};
1929
1930 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
1931 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Overflow)));
1932 let image = asset_server.load("branding/icon.png");
1933
1934 commands
1935 .spawn((
1936 Node {
1937 width: percent(100),
1938 height: percent(100),
1939 align_items: AlignItems::Center,
1940 justify_content: JustifyContent::SpaceAround,
1941 ..Default::default()
1942 },
1943 BackgroundColor(BLUE.into()),
1944 DespawnOnExit(super::Scene::Overflow),
1945 ))
1946 .with_children(|parent| {
1947 for overflow in [
1948 Overflow::visible(),
1949 Overflow::clip_x(),
1950 Overflow::clip_y(),
1951 Overflow::clip(),
1952 ] {
1953 parent
1954 .spawn((
1955 Node {
1956 width: px(100),
1957 height: px(100),
1958 padding: UiRect {
1959 left: px(25),
1960 top: px(25),
1961 ..Default::default()
1962 },
1963 border: UiRect::all(px(5)),
1964 overflow,
1965 ..default()
1966 },
1967 BorderColor::all(RED),
1968 BackgroundColor(Color::WHITE),
1969 ))
1970 .with_children(|parent| {
1971 parent.spawn((
1972 ImageNode::new(image.clone()),
1973 Node {
1974 min_width: px(100),
1975 min_height: px(100),
1976 ..default()
1977 },
1978 Outline {
1979 width: px(2),
1980 offset: px(2),
1981 color: Color::NONE,
1982 },
1983 ));
1984 });
1985 }
1986 });
1987 }
1988}
1989
1990mod slice {
1991 use bevy::prelude::*;
1992
1993 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
1994 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Slice)));
1995 let image = asset_server.load("textures/fantasy_ui_borders/numbered_slices.png");
1996
1997 let slicer = TextureSlicer {
1998 border: BorderRect::all(16.0),
1999 center_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
2000 sides_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
2001 ..default()
2002 };
2003 commands
2004 .spawn((
2005 Node {
2006 width: percent(100),
2007 height: percent(100),
2008 flex_direction: FlexDirection::Column,
2009 justify_content: JustifyContent::SpaceAround,
2010 align_content: AlignContent::Center,
2011 ..default()
2012 },
2013 DespawnOnExit(super::Scene::Slice),
2014 ))
2015 .with_children(|parent| {
2016 for visual_box in [
2017 VisualBox::BorderBox,
2018 VisualBox::PaddingBox,
2019 VisualBox::ContentBox,
2020 ] {
2021 parent
2022 .spawn(Node {
2023 justify_content: JustifyContent::SpaceAround,
2024 ..default()
2025 })
2026 .with_children(|parent| {
2027 for [w, h] in [[200.0, 200.0], [300.0, 200.0], [150., 200.0]] {
2028 parent.spawn((
2029 ImageNode {
2030 image: image.clone(),
2031 image_mode: NodeImageMode::Sliced(slicer.clone()),
2032 visual_box,
2033 ..default()
2034 },
2035 Node {
2036 width: px(w),
2037 height: px(h),
2038 border: px(20.).all(),
2039 padding: px(20.).all(),
2040 ..default()
2041 },
2042 Outline {
2043 width: px(2.),
2044 ..default()
2045 },
2046 ));
2047 }
2048
2049 parent.spawn((
2050 ImageNode {
2051 image: asset_server
2052 .load("textures/fantasy_ui_borders/panel-border-010.png"),
2053 image_mode: NodeImageMode::Sliced(TextureSlicer {
2054 border: BorderRect::all(22.0),
2055 center_scale_mode: SliceScaleMode::Stretch,
2056 sides_scale_mode: SliceScaleMode::Stretch,
2057 max_corner_scale: 1.0,
2058 }),
2059 visual_box,
2060 ..Default::default()
2061 },
2062 Node {
2063 width: px(200),
2064 height: px(200),
2065 border: px(20.).all(),
2066 padding: px(20.).all(),
2067 ..default()
2068 },
2069 Outline {
2070 color: bevy::color::palettes::css::DARK_CYAN.into(),
2071 width: px(2.),
2072 ..default()
2073 },
2074 BackgroundColor(bevy::color::palettes::css::NAVY.into()),
2075 ));
2076 });
2077 }
2078 });
2079 }
2080}
2081
2082mod layout_rounding {
2083 use bevy::{color::palettes::css::*, prelude::*};
2084
2085 pub fn setup(mut commands: Commands) {
2086 commands.spawn((Camera2d, DespawnOnExit(super::Scene::LayoutRounding)));
2087
2088 commands
2089 .spawn((
2090 Node {
2091 display: Display::Grid,
2092 width: percent(100),
2093 height: percent(100),
2094 grid_template_rows: vec![RepeatedGridTrack::fr(10, 1.)],
2095 ..Default::default()
2096 },
2097 BackgroundColor(Color::WHITE),
2098 DespawnOnExit(super::Scene::LayoutRounding),
2099 ))
2100 .with_children(|commands| {
2101 for i in 2..12 {
2102 commands
2103 .spawn(Node {
2104 display: Display::Grid,
2105 grid_template_columns: vec![RepeatedGridTrack::fr(i, 1.)],
2106 ..Default::default()
2107 })
2108 .with_children(|commands| {
2109 for _ in 0..i {
2110 commands.spawn((
2111 Node {
2112 border: UiRect::all(px(5)),
2113 ..Default::default()
2114 },
2115 BackgroundColor(MAROON.into()),
2116 BorderColor::all(DARK_BLUE),
2117 ));
2118 }
2119 });
2120 }
2121 });
2122 }
2123}
2124
2125mod linear_gradient {
2126 use bevy::camera::Camera2d;
2127 use bevy::color::palettes::css::BLUE;
2128 use bevy::color::palettes::css::LIME;
2129 use bevy::color::palettes::css::RED;
2130 use bevy::color::palettes::css::YELLOW;
2131 use bevy::color::Color;
2132 use bevy::ecs::prelude::*;
2133 use bevy::state::state_scoped::DespawnOnExit;
2134 use bevy::text::TextFont;
2135 use bevy::ui::widget::Text;
2136 use bevy::ui::AlignItems;
2137 use bevy::ui::BackgroundGradient;
2138 use bevy::ui::ColorStop;
2139 use bevy::ui::GridPlacement;
2140 use bevy::ui::InterpolationColorSpace;
2141 use bevy::ui::JustifyContent;
2142 use bevy::ui::LinearGradient;
2143 use bevy::ui::Node;
2144 use bevy::ui::PositionType;
2145 use bevy::utils::default;
2146
2147 pub fn setup(mut commands: Commands) {
2148 commands.spawn((Camera2d, DespawnOnExit(super::Scene::LinearGradient)));
2149 commands
2150 .spawn((
2151 Node {
2152 flex_direction: bevy::ui::FlexDirection::Column,
2153 width: bevy::ui::percent(100),
2154 height: bevy::ui::percent(100),
2155 justify_content: JustifyContent::Center,
2156 align_items: AlignItems::Center,
2157 row_gap: bevy::ui::px(5),
2158 ..default()
2159 },
2160 DespawnOnExit(super::Scene::LinearGradient),
2161 ))
2162 .with_children(|commands| {
2163 let mut i = 0;
2164 commands
2165 .spawn(Node {
2166 display: bevy::ui::Display::Grid,
2167 row_gap: bevy::ui::px(4),
2168 column_gap: bevy::ui::px(4),
2169 ..Default::default()
2170 })
2171 .with_children(|commands| {
2172 for stops in [
2173 vec![ColorStop::auto(RED), ColorStop::auto(YELLOW)],
2174 vec![
2175 ColorStop::auto(Color::BLACK),
2176 ColorStop::auto(RED),
2177 ColorStop::auto(Color::WHITE),
2178 ],
2179 vec![
2180 Color::hsl(180.71191, 0.0, 0.3137255).into(),
2181 Color::hsl(180.71191, 0.5, 0.3137255).into(),
2182 Color::hsl(180.71191, 1.0, 0.3137255).into(),
2183 ],
2184 vec![
2185 Color::hsl(180.71191, 0.825, 0.0).into(),
2186 Color::hsl(180.71191, 0.825, 0.5).into(),
2187 Color::hsl(180.71191, 0.825, 1.0).into(),
2188 ],
2189 vec![
2190 Color::hsl(0.0 + 0.0001, 1.0, 0.5).into(),
2191 Color::hsl(180.0, 1.0, 0.5).into(),
2192 Color::hsl(360.0 - 0.0001, 1.0, 0.5).into(),
2193 ],
2194 vec![
2195 Color::WHITE.into(),
2196 RED.into(),
2197 LIME.into(),
2198 BLUE.into(),
2199 Color::BLACK.into(),
2200 ],
2201 ] {
2202 for color_space in [
2203 InterpolationColorSpace::LinearRgba,
2204 InterpolationColorSpace::Srgba,
2205 InterpolationColorSpace::Oklaba,
2206 InterpolationColorSpace::Oklcha,
2207 InterpolationColorSpace::OklchaLong,
2208 InterpolationColorSpace::Hsla,
2209 InterpolationColorSpace::HslaLong,
2210 InterpolationColorSpace::Hsva,
2211 InterpolationColorSpace::HsvaLong,
2212 ] {
2213 let row = i % 18 + 1;
2214 let column = i / 18 + 1;
2215 i += 1;
2216
2217 commands.spawn((
2218 Node {
2219 grid_row: GridPlacement::start(row as i16 + 1),
2220 grid_column: GridPlacement::start(column as i16 + 1),
2221 justify_content: JustifyContent::SpaceEvenly,
2222 ..Default::default()
2223 },
2224 children![(
2225 Node {
2226 height: bevy::ui::px(30),
2227 width: bevy::ui::px(300),
2228 justify_content: JustifyContent::Center,
2229 ..Default::default()
2230 },
2231 BackgroundGradient::from(LinearGradient {
2232 color_space,
2233 angle: LinearGradient::TO_RIGHT,
2234 stops: stops.clone(),
2235 }),
2236 children![
2237 Node {
2238 position_type: PositionType::Absolute,
2239 ..default()
2240 },
2241 TextFont::from_font_size(10.),
2242 Text(format!("{color_space:?}")),
2243 ]
2244 )],
2245 ));
2246 }
2247 }
2248 });
2249 });
2250 }
2251}
2252
2253mod radial_gradient {
2254 use bevy::color::palettes::css::RED;
2255 use bevy::color::palettes::tailwind::GRAY_700;
2256 use bevy::prelude::*;
2257 use bevy::ui::ColorStop;
2258
2259 const CELL_SIZE: f32 = 80.;
2260 const GAP: f32 = 10.;
2261
2262 pub fn setup(mut commands: Commands) {
2263 let color_stops = vec![
2264 ColorStop::new(Color::BLACK, px(5)),
2265 ColorStop::new(Color::WHITE, px(5)),
2266 ColorStop::new(Color::WHITE, percent(100)),
2267 ColorStop::auto(RED),
2268 ];
2269
2270 commands.spawn((Camera2d, DespawnOnExit(super::Scene::RadialGradient)));
2271 commands
2272 .spawn((
2273 Node {
2274 width: percent(100),
2275 height: percent(100),
2276 display: Display::Grid,
2277 align_items: AlignItems::Start,
2278 grid_template_columns: vec![RepeatedGridTrack::px(
2279 GridTrackRepetition::AutoFill,
2280 CELL_SIZE,
2281 )],
2282 grid_auto_flow: GridAutoFlow::Row,
2283 row_gap: px(GAP),
2284 column_gap: px(GAP),
2285 padding: UiRect::all(px(GAP)),
2286 ..default()
2287 },
2288 DespawnOnExit(super::Scene::RadialGradient),
2289 ))
2290 .with_children(|commands| {
2291 for (shape, shape_label) in [
2292 (RadialGradientShape::ClosestSide, "ClosestSide"),
2293 (RadialGradientShape::FarthestSide, "FarthestSide"),
2294 (RadialGradientShape::Circle(percent(55)), "Circle(55%)"),
2295 (RadialGradientShape::FarthestCorner, "FarthestCorner"),
2296 ] {
2297 for (position, position_label) in [
2298 (UiPosition::TOP_LEFT, "TOP_LEFT"),
2299 (UiPosition::LEFT, "LEFT"),
2300 (UiPosition::BOTTOM_LEFT, "BOTTOM_LEFT"),
2301 (UiPosition::TOP, "TOP"),
2302 (UiPosition::CENTER, "CENTER"),
2303 (UiPosition::BOTTOM, "BOTTOM"),
2304 (UiPosition::TOP_RIGHT, "TOP_RIGHT"),
2305 (UiPosition::RIGHT, "RIGHT"),
2306 (UiPosition::BOTTOM_RIGHT, "BOTTOM_RIGHT"),
2307 ] {
2308 for (w, h) in [(CELL_SIZE, CELL_SIZE), (CELL_SIZE, CELL_SIZE / 2.)] {
2309 commands
2310 .spawn((
2311 BackgroundColor(GRAY_700.into()),
2312 Node {
2313 display: Display::Grid,
2314 width: px(CELL_SIZE),
2315 ..Default::default()
2316 },
2317 ))
2318 .with_children(|commands| {
2319 commands.spawn((
2320 Node {
2321 margin: UiRect::all(px(2)),
2322 ..default()
2323 },
2324 Text(format!("{shape_label}\n{position_label}")),
2325 TextFont::from_font_size(9.),
2326 ));
2327 commands.spawn((
2328 Node {
2329 width: px(w),
2330 height: px(h),
2331 ..default()
2332 },
2333 BackgroundGradient::from(RadialGradient {
2334 stops: color_stops.clone(),
2335 position,
2336 shape,
2337 ..default()
2338 }),
2339 ));
2340 });
2341 }
2342 }
2343 }
2344 });
2345 }
2346}
2347
2348mod transformations {
2349 use bevy::{color::palettes::css::*, prelude::*};
2350
2351 pub fn setup(mut commands: Commands) {
2352 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Transformations)));
2353 commands
2354 .spawn((
2355 Node {
2356 width: percent(100),
2357 height: percent(100),
2358 display: Display::Block,
2359 ..default()
2360 },
2361 DespawnOnExit(super::Scene::Transformations),
2362 ))
2363 .with_children(|parent| {
2364 for (transformation, label, background) in [
2365 (
2366 UiTransform::from_rotation(Rot2::degrees(45.)),
2367 "Rotate 45 degrees",
2368 RED,
2369 ),
2370 (
2371 UiTransform::from_scale(Vec2::new(2., 0.5)),
2372 "Scale 2.x 0.5y",
2373 GREEN,
2374 ),
2375 (
2376 UiTransform::from_translation(Val2::px(-50., 50.)),
2377 "Translate -50px x +50px y",
2378 BLUE,
2379 ),
2380 (
2381 UiTransform {
2382 translation: Val2::px(50., 0.),
2383 scale: Vec2::new(-1., 1.),
2384 rotation: Rot2::degrees(30.),
2385 },
2386 "T 50px x\nS -1.x (refl)\nR 30deg",
2387 DARK_CYAN,
2388 ),
2389 ] {
2390 parent
2391 .spawn((Node {
2392 width: percent(100),
2393 margin: UiRect {
2394 top: px(50),
2395 bottom: px(50),
2396 ..default()
2397 },
2398 align_items: AlignItems::Center,
2399 justify_content: JustifyContent::SpaceAround,
2400 ..default()
2401 },))
2402 .with_children(|row| {
2403 row.spawn((
2404 Text::new("Before Tf"),
2405 Node {
2406 width: px(100),
2407 height: px(100),
2408 border_radius: BorderRadius::bottom_right(px(25.)),
2409 ..default()
2410 },
2411 BackgroundColor(background.into()),
2412 TextFont::default(),
2413 ));
2414 row.spawn((
2415 Text::new(label),
2416 Node {
2417 width: px(100),
2418 height: px(100),
2419 border_radius: BorderRadius::bottom_right(px(25.)),
2420 ..default()
2421 },
2422 BackgroundColor(background.into()),
2423 transformation,
2424 TextFont::default(),
2425 ));
2426 });
2427 }
2428 });
2429 }
2430}
2431
2432#[cfg(feature = "bevy_ui_debug")]
2433mod debug_outlines {
2434 use bevy::{
2435 color::palettes::css::{BLUE, GRAY, RED},
2436 prelude::*,
2437 ui_render::UiDebugOptions,
2438 };
2439
2440 pub fn setup(mut commands: Commands, mut debug_options: ResMut<GlobalUiDebugOptions>) {
2441 debug_options.enabled = true;
2442 debug_options.line_width = 5.;
2443 debug_options.line_color_override = Some(LinearRgba::GREEN);
2444 debug_options.show_hidden = true;
2445 debug_options.show_clipped = true;
2446
2447 let debug_options: UiDebugOptions = (*debug_options.as_ref()).into();
2448
2449 commands.spawn((Camera2d, DespawnOnExit(super::Scene::DebugOutlines)));
2450 commands
2451 .spawn((
2452 Node {
2453 width: percent(100),
2454 height: percent(50),
2455 align_items: AlignItems::Center,
2456 justify_content: JustifyContent::SpaceAround,
2457 ..default()
2458 },
2459 DespawnOnExit(super::Scene::DebugOutlines),
2460 ))
2461 .with_children(|parent| {
2462 parent.spawn((
2463 Node {
2464 width: px(100),
2465 height: px(100),
2466 ..default()
2467 },
2468 BackgroundColor(GRAY.into()),
2469 UiTransform::from_rotation(Rot2::degrees(45.)),
2470 ));
2471
2472 parent.spawn((Text::new("Regular Text"), TextFont::default()));
2473
2474 parent.spawn((
2475 Node {
2476 width: px(100),
2477 height: px(100),
2478 ..default()
2479 },
2480 Text::new("Invisible"),
2481 BackgroundColor(GRAY.into()),
2482 TextFont::default(),
2483 Visibility::Hidden,
2484 ));
2485
2486 parent
2487 .spawn((
2488 Node {
2489 width: px(100),
2490 height: px(100),
2491 padding: UiRect {
2492 left: px(25),
2493 top: px(25),
2494 ..Default::default()
2495 },
2496 overflow: Overflow::clip(),
2497 ..default()
2498 },
2499 BackgroundColor(RED.into()),
2500 ))
2501 .with_children(|child| {
2502 child.spawn((
2503 Node {
2504 min_width: px(100),
2505 min_height: px(100),
2506 ..default()
2507 },
2508 BackgroundColor(BLUE.into()),
2509 ));
2510 });
2511 });
2512
2513 commands
2514 .spawn((
2515 Node {
2516 width: percent(100),
2517 height: percent(50),
2518 top: percent(50),
2519 align_items: AlignItems::Center,
2520 justify_content: JustifyContent::SpaceAround,
2521 ..default()
2522 },
2523 DespawnOnExit(super::Scene::DebugOutlines),
2524 ))
2525 .with_children(|parent| {
2526 parent.spawn((
2527 Node {
2528 width: px(200),
2529 height: px(200),
2530 border: UiRect {
2531 top: px(10),
2532 bottom: px(20),
2533 left: px(30),
2534 right: px(40),
2535 },
2536 border_radius: BorderRadius::bottom_right(px(10)),
2537 padding: UiRect {
2538 top: px(40),
2539 bottom: px(30),
2540 left: px(20),
2541 right: px(10),
2542 },
2543 ..default()
2544 },
2545 children![(
2546 Text::new("border padding content outlines"),
2547 TextFont::default(),
2548 UiDebugOptions {
2549 enabled: false,
2550 ..default()
2551 }
2552 )],
2553 UiDebugOptions {
2554 outline_border_box: true,
2555 outline_padding_box: true,
2556 outline_content_box: true,
2557 ignore_border_radius: false,
2558 ..debug_options
2559 },
2560 ));
2561
2562 // Vertical scrollbar (non-functional)
2563 parent.spawn((
2564 Node {
2565 flex_direction: FlexDirection::Column,
2566 width: px(90),
2567 height: px(230),
2568 overflow: Overflow::scroll_y(),
2569 scrollbar_width: 20.,
2570 ..default()
2571 },
2572 ScrollPosition(Vec2::new(180., 180.)),
2573 UiDebugOptions {
2574 line_width: 3.,
2575 outline_scrollbars: true,
2576 show_hidden: false,
2577 show_clipped: false,
2578 ..debug_options
2579 },
2580 Children::spawn(SpawnIter((0..20).map(move |i| {
2581 (
2582 Node::default(),
2583 children![(
2584 Text(format!("Item {i}")),
2585 UiDebugOptions {
2586 enabled: false,
2587 ..default()
2588 }
2589 )],
2590 UiDebugOptions {
2591 enabled: false,
2592 ..default()
2593 },
2594 )
2595 }))),
2596 ));
2597
2598 // Horizontal scrollbar (non-functional)
2599 parent.spawn((
2600 Node {
2601 flex_direction: FlexDirection::Row,
2602 width: px(156),
2603 height: px(70),
2604 overflow: Overflow::scroll_x(),
2605 scrollbar_width: 10.,
2606 ..default()
2607 },
2608 UiDebugOptions {
2609 line_width: 3.,
2610 outline_scrollbars: true,
2611 show_hidden: false,
2612 show_clipped: false,
2613 ..debug_options
2614 },
2615 Children::spawn(SpawnIter((0..20).map(move |i| {
2616 (
2617 Node::default(),
2618 children![(
2619 Text(format!("Item {i}")),
2620 UiDebugOptions {
2621 enabled: false,
2622 ..default()
2623 }
2624 )],
2625 UiDebugOptions {
2626 enabled: false,
2627 ..default()
2628 },
2629 )
2630 }))),
2631 ));
2632
2633 // bi-directional scrollbar (non-functional)
2634 parent.spawn((
2635 Node {
2636 flex_direction: FlexDirection::Column,
2637 width: px(230),
2638 height: px(125),
2639 overflow: Overflow::scroll(),
2640 scrollbar_width: 20.,
2641 ..default()
2642 },
2643 ScrollPosition(Vec2::new(300., 0.)),
2644 UiDebugOptions {
2645 line_width: 3.,
2646 outline_scrollbars: true,
2647 show_hidden: false,
2648 show_clipped: false,
2649 ..debug_options
2650 },
2651 Children::spawn(SpawnIter((0..6).map(move |i| {
2652 (
2653 Node {
2654 flex_direction: FlexDirection::Row,
2655 ..default()
2656 },
2657 Children::spawn(SpawnIter((0..6).map({
2658 move |j| {
2659 (
2660 Text(format!("Item {}", (i * 5) + j)),
2661 UiDebugOptions {
2662 enabled: false,
2663 ..default()
2664 },
2665 )
2666 }
2667 }))),
2668 UiDebugOptions {
2669 enabled: false,
2670 ..default()
2671 },
2672 )
2673 }))),
2674 ));
2675 });
2676 }
2677
2678 pub fn teardown(mut debug_options: ResMut<GlobalUiDebugOptions>) {
2679 *debug_options = GlobalUiDebugOptions::default();
2680 }
2681}
2682
2683mod viewport_coords {
2684 use bevy::{color::palettes::css::*, prelude::*};
2685
2686 const PALETTE: [Srgba; 9] = [RED, WHITE, BEIGE, AQUA, CRIMSON, NAVY, AZURE, LIME, BLACK];
2687
2688 pub fn setup(mut commands: Commands) {
2689 commands.spawn((Camera2d, DespawnOnExit(super::Scene::ViewportCoords)));
2690 commands
2691 .spawn((
2692 Node {
2693 width: vw(100),
2694 height: vh(100),
2695 border: UiRect::axes(vw(5), vh(5)),
2696 flex_wrap: FlexWrap::Wrap,
2697 ..default()
2698 },
2699 BorderColor::all(PALETTE[0]),
2700 DespawnOnExit(super::Scene::ViewportCoords),
2701 ))
2702 .with_children(|builder| {
2703 builder.spawn((
2704 Node {
2705 width: vw(30),
2706 height: vh(30),
2707 border: UiRect::all(vmin(5)),
2708 ..default()
2709 },
2710 BackgroundColor(PALETTE[1].into()),
2711 BorderColor::all(PALETTE[8]),
2712 ));
2713
2714 builder.spawn((
2715 Node {
2716 width: vw(60),
2717 height: vh(30),
2718 ..default()
2719 },
2720 BackgroundColor(PALETTE[2].into()),
2721 ));
2722
2723 builder.spawn((
2724 Node {
2725 width: vw(45),
2726 height: vh(30),
2727 border: UiRect::left(vmax(45. / 2.)),
2728 ..default()
2729 },
2730 BackgroundColor(PALETTE[3].into()),
2731 BorderColor::all(PALETTE[7]),
2732 ));
2733
2734 builder.spawn((
2735 Node {
2736 width: vw(45),
2737 height: vh(30),
2738 border: UiRect::right(vmax(45. / 2.)),
2739 ..default()
2740 },
2741 BackgroundColor(PALETTE[4].into()),
2742 BorderColor::all(PALETTE[7]),
2743 ));
2744
2745 builder.spawn((
2746 Node {
2747 width: vw(60),
2748 height: vh(30),
2749 ..default()
2750 },
2751 BackgroundColor(PALETTE[5].into()),
2752 ));
2753
2754 builder.spawn((
2755 Node {
2756 width: vw(30),
2757 height: vh(30),
2758 border: UiRect::all(vmin(5)),
2759 ..default()
2760 },
2761 BackgroundColor(PALETTE[6].into()),
2762 BorderColor::all(PALETTE[8]),
2763 ));
2764 });
2765 }
2766}
2767
2768mod viewport_node {
2769 use bevy::{
2770 camera::RenderTarget, prelude::*, render::render_resource::TextureFormat,
2771 ui::widget::ViewportNode,
2772 };
2773
2774 pub fn setup(
2775 mut commands: Commands,
2776 mut images: ResMut<Assets<Image>>,
2777 mut meshes: ResMut<Assets<Mesh>>,
2778 mut materials: ResMut<Assets<StandardMaterial>>,
2779 ) {
2780 commands.spawn((
2781 Camera3d::default(),
2782 DespawnOnExit(super::Scene::ViewportNode),
2783 ));
2784
2785 let image = Image::new_target_texture(0, 0, TextureFormat::Bgra8UnormSrgb, None);
2786 let image_handle = images.add(image);
2787
2788 let camera = commands
2789 .spawn((
2790 Camera3d::default(),
2791 Camera {
2792 order: -1,
2793 ..default()
2794 },
2795 RenderTarget::Image(image_handle.into()),
2796 DespawnOnExit(super::Scene::ViewportNode),
2797 ))
2798 .id();
2799
2800 commands.spawn((
2801 Mesh3d(meshes.add(Cuboid::new(5.0, 5.0, 5.0))),
2802 MeshMaterial3d(materials.add(Color::WHITE)),
2803 Transform {
2804 translation: Vec3::new(0.0, 0.0, -10.0),
2805 rotation: Quat::from_euler(EulerRot::XYZ, 0.4, 0.7, 0.1),
2806 ..default()
2807 },
2808 DespawnOnExit(super::Scene::ViewportNode),
2809 ));
2810
2811 commands.spawn((
2812 Node {
2813 position_type: PositionType::Absolute,
2814 top: px(50),
2815 left: px(50),
2816 width: px(200),
2817 height: px(200),
2818 border: UiRect::all(px(5)),
2819 ..default()
2820 },
2821 BorderColor::all(Color::WHITE),
2822 ViewportNode::new(camera),
2823 DespawnOnExit(super::Scene::ViewportNode),
2824 ));
2825 }
2826}
2827
2828mod outer_color {
2829 use bevy::prelude::*;
2830
2831 pub fn setup(mut commands: Commands) {
2832 let radius = CornerRadius::from(percent(33.));
2833 let width = px(10.);
2834
2835 commands.spawn((Camera2d, DespawnOnExit(super::Scene::OuterColor)));
2836 commands
2837 .spawn((
2838 Node {
2839 display: Display::Grid,
2840 grid_template_columns: RepeatedGridTrack::px(3, 200.),
2841 grid_template_rows: RepeatedGridTrack::px(3, 200.),
2842 margin: UiRect::AUTO,
2843 ..default()
2844 },
2845 DespawnOnExit(super::Scene::OuterColor),
2846 ))
2847 .with_children(|builder| {
2848 for (border, border_radius, invert) in [
2849 (UiRect::ZERO, BorderRadius::bottom_right(radius), true),
2850 (UiRect::top(width), BorderRadius::top(radius), false),
2851 (UiRect::ZERO, BorderRadius::bottom_left(radius), true),
2852 (UiRect::left(width), BorderRadius::left(radius), false),
2853 (UiRect::all(width), BorderRadius::all(radius.x), true),
2854 (UiRect::right(width), BorderRadius::right(radius), false),
2855 (UiRect::ZERO, BorderRadius::top_right(radius), true),
2856 (UiRect::bottom(width), BorderRadius::bottom(radius), false),
2857 (UiRect::ZERO, BorderRadius::top_left(radius), true),
2858 ] {
2859 builder
2860 .spawn((
2861 Node {
2862 width: px(200.),
2863 height: px(200.),
2864 border_radius,
2865 border,
2866 ..default()
2867 },
2868 BorderColor::all(bevy::color::palettes::css::RED),
2869 ))
2870 .insert_if(BackgroundColor(Color::WHITE), || !invert)
2871 .insert_if(OuterColor(Color::WHITE), || invert);
2872 }
2873 });
2874 }
2875}
2876
2877mod boxed_content {
2878 use bevy::color::palettes::css::RED;
2879 use bevy::prelude::*;
2880
2881 pub fn setup(mut commands: Commands) {
2882 commands.spawn((Camera2d, DespawnOnExit(super::Scene::BoxedContent)));
2883 commands
2884 .spawn((
2885 Node {
2886 margin: auto().all(),
2887 column_gap: px(30),
2888 ..default()
2889 },
2890 DespawnOnExit(super::Scene::BoxedContent),
2891 ))
2892 .with_children(|builder| {
2893 for (heading, text_justify) in [
2894 ("Left", Justify::Left),
2895 ("Center", Justify::Center),
2896 ("Right", Justify::Right),
2897 ] {
2898 builder
2899 .spawn(Node {
2900 flex_direction: FlexDirection::Column,
2901 align_items: AlignItems::Center,
2902 justify_content: JustifyContent::Start,
2903 row_gap: px(20),
2904 ..default()
2905 })
2906 .with_children(|builder| {
2907 builder.spawn((
2908 Node::default(),
2909 Text::new(format!("{heading} justify")),
2910 TextFont::from_font_size(FontSize::Px(14.)),
2911 TextLayout::justify(Justify::Center),
2912 ));
2913
2914 builder.spawn((
2915 Node::default(),
2916 Text::new("This text has\nno border or padding."),
2917 TextFont::from_font_size(FontSize::Px(10.)),
2918 TextLayout::justify(text_justify),
2919 Outline {
2920 width: px(2),
2921 color: Color::WHITE,
2922 ..Default::default()
2923 },
2924 ));
2925
2926 builder.spawn((
2927 Node {
2928 border: px(10).all(),
2929 ..default()
2930 },
2931 Text::new("This text has\na border but no padding."),
2932 TextFont::from_font_size(FontSize::Px(10.)),
2933 TextLayout::justify(text_justify),
2934 BorderColor::all(RED),
2935 Outline {
2936 width: px(2),
2937 color: Color::WHITE,
2938 ..Default::default()
2939 },
2940 ));
2941
2942 builder.spawn((
2943 Node {
2944 padding: px(20).all(),
2945 ..default()
2946 },
2947 Text::new("This text has\npadding but no border."),
2948 TextFont::from_font_size(FontSize::Px(10.)),
2949 TextLayout::justify(text_justify),
2950 Outline {
2951 width: px(2),
2952 color: Color::WHITE,
2953 ..Default::default()
2954 },
2955 ));
2956
2957 builder.spawn((
2958 Node {
2959 border: px(10).all(),
2960 padding: px(20).all(),
2961 ..default()
2962 },
2963 Text::new("This text has\nborder and padding."),
2964 TextFont::from_font_size(FontSize::Px(10.)),
2965 TextLayout::justify(text_justify),
2966 BorderColor::all(RED),
2967 Outline {
2968 width: px(2),
2969 color: Color::WHITE,
2970 ..Default::default()
2971 },
2972 ));
2973
2974 builder.spawn((
2975 Node {
2976 border: px(10).left(),
2977 ..default()
2978 },
2979 Text::new("This text has\na left border and no padding."),
2980 TextFont::from_font_size(FontSize::Px(10.)),
2981 TextLayout::justify(text_justify),
2982 BorderColor::all(RED),
2983 Outline {
2984 width: px(2),
2985 color: Color::WHITE,
2986 ..Default::default()
2987 },
2988 ));
2989
2990 builder.spawn((
2991 Node {
2992 border: px(10).right(),
2993 ..default()
2994 },
2995 Text::new("This text has\na right border and no padding."),
2996 TextFont::from_font_size(FontSize::Px(10.)),
2997 TextLayout::justify(text_justify),
2998 BorderColor::all(RED),
2999 Outline {
3000 width: px(2),
3001 color: Color::WHITE,
3002 ..Default::default()
3003 },
3004 ));
3005
3006 builder.spawn((
3007 Node {
3008 padding: px(20).top().with_right(px(20)),
3009 ..default()
3010 },
3011 Text::new("This text has\npadding on its top and right."),
3012 TextFont::from_font_size(FontSize::Px(10.)),
3013 TextLayout::justify(text_justify),
3014 BorderColor::all(RED),
3015 Outline {
3016 width: px(2),
3017 color: Color::WHITE,
3018 ..Default::default()
3019 },
3020 ));
3021
3022 builder.spawn((
3023 Node {
3024 padding: px(20).bottom().with_left(px(20)),
3025 ..default()
3026 },
3027 Text::new("This text has\npadding on its bottom and left."),
3028 TextFont::from_font_size(FontSize::Px(10.)),
3029 TextLayout::justify(text_justify),
3030 BorderColor::all(RED),
3031 Outline {
3032 width: px(2),
3033 color: Color::WHITE,
3034 ..Default::default()
3035 },
3036 ));
3037
3038 builder.spawn((
3039 Node {
3040 padding: px(20).top().with_left(px(20)),
3041 border: px(10).bottom().with_right(px(10)),
3042 ..default()
3043 },
3044 Text::new(
3045 "This text has\npadding on its top and left\nand a border on its bottom and right.",
3046 ),
3047 TextFont::from_font_size(FontSize::Px(10.)),
3048 TextLayout::justify(text_justify),
3049 BorderColor::all(RED),
3050 Outline {
3051 width: px(2),
3052 color: Color::WHITE,
3053 ..Default::default()
3054 },
3055 ));
3056 });
3057 }
3058 });
3059 }
3060}
3061
3062mod editable_text {
3063 use bevy::color::palettes::css::YELLOW;
3064 use bevy::prelude::*;
3065 use bevy::text::EditableText;
3066 use bevy::text::TextCursorStyle;
3067 use bevy::text::TextEdit;
3068 use bevy::ui_widgets::TextInput;
3069
3070 const DUMMY_TEXT: &str = "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten";
3071 const LOREM_TEXT: &str = concat!(
3072 "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ",
3073 "Aenean commodo ligula eget dolor. Aenean massa. ",
3074 "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur reprehenderit mus. ",
3075 "Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. ",
3076 "Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. ",
3077 "In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. ",
3078 "Nullam dictum felis eu pede mollis pretium. Integer tincidunt. ",
3079 "Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. ",
3080 "Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. ",
3081 "Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. ",
3082 "Phasellus viverra nulla ut metus officia laoreet. Quisque rutrum. ",
3083 "Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi.",
3084 " Qui eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, ",
3085 "sem quam semper libero, sit amet adipiscing sem neque sed ipsum. ",
3086 "Qui quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. ",
3087 "Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. ",
3088 "Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. ",
3089 "Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. ",
3090 "Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,"
3091 );
3092
3093 pub fn setup(mut commands: Commands) {
3094 commands.spawn((Camera2d, DespawnOnExit(super::Scene::EditableText)));
3095 commands.spawn((
3096 Node {
3097 flex_wrap: FlexWrap::Wrap,
3098 align_items: AlignItems::Start,
3099 margin: px(10.).all(),
3100 width: vw(100),
3101 height: vh(100),
3102 row_gap: px(10),
3103 column_gap: px(20),
3104 ..default()
3105 },
3106 DespawnOnExit(super::Scene::EditableText),
3107 children![
3108 (
3109 Node {
3110 flex_direction: FlexDirection::Column,
3111 row_gap: px(10),
3112 ..default()
3113 },
3114 children![
3115 Text::new("Single line"),
3116 (
3117 TextInput,
3118 EditableText {
3119 pending_edits: vec![TextEdit::Insert(
3120 "Single line EditableText".into(),
3121 )],
3122 ..default()
3123 },
3124 TextLayout::no_wrap(),
3125 TextFont {
3126 font_size: FontSize::Px(10.),
3127 ..default()
3128 },
3129 TextCursorStyle::default(),
3130 Node {
3131 width: px(200.),
3132 border: px(2).all(),
3133 ..default()
3134 },
3135 BorderColor::all(YELLOW),
3136 ),
3137 Text::new("Initial end"),
3138 (
3139 EditableText::new(LOREM_TEXT),
3140 TextLayout::no_wrap(),
3141 TextFont {
3142 font_size: FontSize::Px(10.),
3143 ..default()
3144 },
3145 TextCursorStyle::default(),
3146 Node {
3147 width: px(200.),
3148 border: px(2).all(),
3149 ..default()
3150 },
3151 BorderColor::all(YELLOW),
3152 ),
3153 Text::new("Insert end"),
3154 (
3155 TextInput,
3156 EditableText {
3157 pending_edits: vec![TextEdit::Insert(LOREM_TEXT.into())],
3158 ..default()
3159 },
3160 TextLayout::no_wrap(),
3161 TextFont {
3162 font_size: FontSize::Px(10.),
3163 ..default()
3164 },
3165 TextCursorStyle::default(),
3166 Node {
3167 width: px(200.),
3168 border: px(2).all(),
3169 ..default()
3170 },
3171 BorderColor::all(YELLOW),
3172 ),
3173 Text::new("Select line start"),
3174 (
3175 TextInput,
3176 EditableText {
3177 pending_edits: vec![
3178 TextEdit::Insert(LOREM_TEXT.into()),
3179 TextEdit::LineStart(true),
3180 ],
3181 ..default()
3182 },
3183 TextLayout::no_wrap(),
3184 TextFont {
3185 font_size: FontSize::Px(10.),
3186 ..default()
3187 },
3188 TextCursorStyle::default(),
3189 Node {
3190 width: px(200.),
3191 border: px(2).all(),
3192 ..default()
3193 },
3194 BorderColor::all(YELLOW),
3195 ),
3196 ],
3197 ),
3198 (
3199 Node {
3200 flex_direction: FlexDirection::Column,
3201 row_gap: px(10),
3202 ..default()
3203 },
3204 children![
3205 Text::new("Wrapped start"),
3206 (
3207 TextInput,
3208 EditableText {
3209 pending_edits: vec![
3210 TextEdit::Insert(LOREM_TEXT.into()),
3211 TextEdit::TextStart(false),
3212 ],
3213 visible_lines: Some(8.),
3214 ..default()
3215 },
3216 Node {
3217 width: px(200.),
3218 border: px(2).all(),
3219 ..default()
3220 },
3221 TextCursorStyle::default(),
3222 TextFont {
3223 font_size: FontSize::Px(10.),
3224 ..default()
3225 },
3226 BorderColor::all(YELLOW),
3227 ),
3228 ],
3229 ),
3230 (
3231 Node {
3232 flex_direction: FlexDirection::Column,
3233 row_gap: px(10),
3234 ..default()
3235 },
3236 children![
3237 Text::new("Wrapped selection"),
3238 (
3239 TextInput,
3240 EditableText {
3241 pending_edits: vec![
3242 TextEdit::Insert(LOREM_TEXT.into()),
3243 TextEdit::TextStart(false),
3244 TextEdit::Down(false),
3245 TextEdit::TextEnd(true),
3246 ],
3247 visible_lines: Some(8.),
3248 ..default()
3249 },
3250 TextCursorStyle::default(),
3251 Node {
3252 width: px(200.),
3253 border: px(2).all(),
3254 ..default()
3255 },
3256 TextFont {
3257 font_size: FontSize::Px(10.),
3258 ..default()
3259 },
3260 BorderColor::all(YELLOW),
3261 ),
3262 ],
3263 ),
3264 (
3265 Node {
3266 flex_direction: FlexDirection::Column,
3267 row_gap: px(10),
3268 ..default()
3269 },
3270 children![
3271 Text::new("Clamp top"),
3272 (
3273 TextInput,
3274 EditableText {
3275 pending_edits: vec![
3276 TextEdit::Insert(DUMMY_TEXT.into()),
3277 TextEdit::ScrollByLines(-10.0),
3278 ],
3279 visible_lines: Some(5.5),
3280 ..default()
3281 },
3282 TextCursorStyle::default(),
3283 TextFont {
3284 font_size: FontSize::Px(10.),
3285 ..default()
3286 },
3287 Node {
3288 width: px(100.),
3289 border: px(2).all(),
3290 ..default()
3291 },
3292 BorderColor::all(YELLOW),
3293 ),
3294 ],
3295 ),
3296 (
3297 Node {
3298 flex_direction: FlexDirection::Column,
3299 row_gap: px(10),
3300 ..default()
3301 },
3302 children![
3303 Text::new("Home, Scroll 1"),
3304 (
3305 TextInput,
3306 EditableText {
3307 pending_edits: vec![
3308 TextEdit::Insert(DUMMY_TEXT.into()),
3309 TextEdit::ScrollTo(Vec2::ZERO),
3310 TextEdit::ScrollByLines(1.0),
3311 ],
3312 visible_lines: Some(5.5),
3313 ..default()
3314 },
3315 TextCursorStyle::default(),
3316 TextFont {
3317 font_size: FontSize::Px(10.),
3318 ..default()
3319 },
3320 Node {
3321 width: px(100.),
3322 border: px(2).all(),
3323 ..default()
3324 },
3325 BorderColor::all(YELLOW),
3326 ),
3327 ],
3328 ),
3329 (
3330 Node {
3331 flex_direction: FlexDirection::Column,
3332 row_gap: px(10),
3333 ..default()
3334 },
3335 children![
3336 Text::new("Home, Scroll 2"),
3337 (
3338 TextInput,
3339 EditableText {
3340 pending_edits: vec![
3341 TextEdit::Insert(DUMMY_TEXT.into()),
3342 TextEdit::ScrollTo(Vec2::ZERO),
3343 TextEdit::ScrollByLines(2.0),
3344 ],
3345 visible_lines: Some(5.5),
3346 ..default()
3347 },
3348 TextFont {
3349 font_size: FontSize::Px(10.),
3350 ..default()
3351 },
3352 TextCursorStyle::default(),
3353 Node {
3354 width: px(100.),
3355 border: px(2).all(),
3356 ..default()
3357 },
3358 BorderColor::all(YELLOW),
3359 ),
3360 ],
3361 ),
3362 (
3363 Node {
3364 flex_direction: FlexDirection::Column,
3365 row_gap: px(10),
3366 ..default()
3367 },
3368 children![
3369 Text::new("Clamp bottom"),
3370 (
3371 TextInput,
3372 EditableText {
3373 pending_edits: vec![
3374 TextEdit::Insert(DUMMY_TEXT.into()),
3375 TextEdit::ScrollByLines(-1000.0),
3376 TextEdit::ScrollByLines(1000.0),
3377 ],
3378 visible_lines: Some(5.5),
3379 ..default()
3380 },
3381 TextCursorStyle::default(),
3382 TextFont {
3383 font_size: FontSize::Px(10.),
3384 ..default()
3385 },
3386 Node {
3387 width: px(100.),
3388 border: px(2).all(),
3389 ..default()
3390 },
3391 BorderColor::all(YELLOW),
3392 ),
3393 ],
3394 ),
3395 (
3396 Node {
3397 flex_direction: FlexDirection::Column,
3398 row_gap: px(10),
3399 ..default()
3400 },
3401 children![
3402 Text::new("Bottom -1"),
3403 (
3404 TextInput,
3405 EditableText {
3406 pending_edits: vec![
3407 TextEdit::Insert(DUMMY_TEXT.into()),
3408 TextEdit::ScrollByLines(-1000.0),
3409 TextEdit::ScrollByLines(1000.0),
3410 TextEdit::ScrollByLines(-1.0),
3411 ],
3412 visible_lines: Some(5.5),
3413 ..default()
3414 },
3415 TextCursorStyle::default(),
3416 TextFont {
3417 font_size: FontSize::Px(10.),
3418 ..default()
3419 },
3420 Node {
3421 width: px(100.),
3422 border: px(2).all(),
3423 ..default()
3424 },
3425 BorderColor::all(YELLOW),
3426 ),
3427 ],
3428 ),
3429 (
3430 Node {
3431 flex_direction: FlexDirection::Column,
3432 row_gap: px(10),
3433 ..default()
3434 },
3435 children![
3436 Text::new("Top +3"),
3437 (
3438 TextInput,
3439 EditableText {
3440 pending_edits: vec![
3441 TextEdit::Insert(DUMMY_TEXT.into()),
3442 TextEdit::ScrollByLines(-1000.0),
3443 TextEdit::ScrollByLines(3.0),
3444 ],
3445 visible_lines: Some(5.5),
3446 ..default()
3447 },
3448 TextCursorStyle::default(),
3449 TextFont {
3450 font_size: FontSize::Px(10.),
3451 ..default()
3452 },
3453 Node {
3454 width: px(100.),
3455 border: px(2).all(),
3456 ..default()
3457 },
3458 BorderColor::all(YELLOW),
3459 ),
3460 ],
3461 ),
3462 (
3463 Node {
3464 flex_direction: FlexDirection::Column,
3465 row_gap: px(10),
3466 ..default()
3467 },
3468 children![
3469 Text::new("Select down 3"),
3470 (
3471 TextInput,
3472 EditableText {
3473 pending_edits: vec![
3474 TextEdit::Insert(DUMMY_TEXT.into()),
3475 TextEdit::TextStart(false),
3476 TextEdit::Down(false),
3477 TextEdit::Down(true),
3478 TextEdit::Down(true),
3479 TextEdit::Down(true),
3480 ],
3481 visible_lines: Some(5.5),
3482 ..default()
3483 },
3484 TextCursorStyle::default(),
3485 TextFont {
3486 font_size: FontSize::Px(10.),
3487 ..default()
3488 },
3489 Node {
3490 width: px(100.),
3491 border: px(2).all(),
3492 ..default()
3493 },
3494 BorderColor::all(YELLOW),
3495 ),
3496 ],
3497 ),
3498 (
3499 Node {
3500 flex_direction: FlexDirection::Column,
3501 row_gap: px(10),
3502 ..default()
3503 },
3504 children![
3505 Text::new("End, Scroll 1"),
3506 (
3507 TextInput,
3508 EditableText {
3509 pending_edits: vec![
3510 TextEdit::Insert(DUMMY_TEXT.into()),
3511 TextEdit::ScrollByLines(1.0),
3512 ],
3513 visible_lines: Some(5.5),
3514 ..default()
3515 },
3516 TextCursorStyle::default(),
3517 TextFont {
3518 font_size: FontSize::Px(10.),
3519 ..default()
3520 },
3521 Node {
3522 width: px(100.),
3523 border: px(2).all(),
3524 ..default()
3525 },
3526 BorderColor::all(YELLOW),
3527 ),
3528 ],
3529 ),
3530 (
3531 Node {
3532 flex_direction: FlexDirection::Column,
3533 row_gap: px(10),
3534 ..default()
3535 },
3536 children![
3537 Text::new("End, Scroll -0.5"),
3538 (
3539 TextInput,
3540 EditableText {
3541 pending_edits: vec![
3542 TextEdit::Insert(DUMMY_TEXT.into()),
3543 TextEdit::ScrollByLines(-0.5),
3544 ],
3545 visible_lines: Some(5.5),
3546 ..default()
3547 },
3548 TextCursorStyle::default(),
3549 TextFont {
3550 font_size: FontSize::Px(10.),
3551 ..default()
3552 },
3553 Node {
3554 width: px(100.),
3555 border: px(2).all(),
3556 ..default()
3557 },
3558 BorderColor::all(YELLOW),
3559 ),
3560 ],
3561 )
3562 ],
3563 ));
3564 }
3565}
3566
3567mod node_material {
3568 use bevy::{
3569 color::palettes::tailwind::{CYAN_100, RED_500, YELLOW_100},
3570 prelude::*,
3571 render::render_resource::AsBindGroup,
3572 shader::ShaderRef,
3573 };
3574 const SHADER_ASSET_PATH: &str = "shaders/custom_ui_material.wesl";
3575
3576 #[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
3577 pub(super) struct DefaultUiMaterial {}
3578
3579 impl UiMaterial for DefaultUiMaterial {}
3580
3581 #[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
3582 pub(super) struct CustomUiMaterial {
3583 /// Color multiplied with the image
3584 #[uniform(0)]
3585 color: Vec4,
3586 /// Represents how much of the image is visible
3587 /// Goes from 0 to 1
3588 /// A `Vec4` is used here because Bevy with webgl2 requires that uniforms are 16-byte aligned but only the first component is read.
3589 #[uniform(1)]
3590 slider: Vec4,
3591 /// Image used to represent the slider
3592 #[texture(2)]
3593 #[sampler(3)]
3594 color_texture: Handle<Image>,
3595 /// Color of the image's border
3596 #[uniform(4)]
3597 border_color: Vec4,
3598 }
3599
3600 impl UiMaterial for CustomUiMaterial {
3601 fn fragment_shader() -> ShaderRef {
3602 SHADER_ASSET_PATH.into()
3603 }
3604 }
3605
3606 pub fn setup(
3607 mut commands: Commands,
3608 mut default_ui_materials: ResMut<Assets<DefaultUiMaterial>>,
3609 mut ui_materials: ResMut<Assets<CustomUiMaterial>>,
3610 asset_server: Res<AssetServer>,
3611 ) {
3612 let default_material = default_ui_materials.add(DefaultUiMaterial {});
3613 let custom_material = |slider| CustomUiMaterial {
3614 color: LinearRgba::from(CYAN_100).to_vec4(),
3615 slider: Vec4::splat(slider),
3616 color_texture: asset_server.load("branding/banner.png"),
3617 border_color: LinearRgba::from(YELLOW_100).to_vec4(),
3618 };
3619 let full_material = ui_materials.add(custom_material(1.));
3620 let material_node = Node {
3621 width: px(200),
3622 height: px(80),
3623 border: px(10).all(),
3624 border_radius: BorderRadius::all(px(20)),
3625 ..default()
3626 };
3627 let material_label = |text: &str| (Text::new(text), TextFont::from_font_size(px(10.)));
3628
3629 commands.spawn((Camera2d, DespawnOnExit(super::Scene::NodeMaterial)));
3630 commands.spawn((
3631 Node {
3632 display: Display::Grid,
3633 // two column grid, labels then materials, first row is spanned by title.
3634 width: percent(100),
3635 height: percent(100),
3636 grid_template_columns: vec![GridTrack::auto(), GridTrack::px(200.)],
3637 align_items: AlignItems::Center,
3638 align_content: AlignContent::Center,
3639 justify_content: JustifyContent::Center,
3640 row_gap: px(10.),
3641 column_gap: px(20.),
3642 ..default()
3643 },
3644 DespawnOnExit(super::Scene::NodeMaterial),
3645 children![
3646 (
3647 Text::new("MaterialNode"),
3648 TextFont::from_font_size(px(30.)),
3649 TextLayout::justify(Justify::Center),
3650 Node {
3651 grid_column: GridPlacement::span(2),
3652 margin: px(10.).bottom(),
3653 ..default()
3654 },
3655 ),
3656 // Default UI material (just white with the default ui_material.wgsl shader for the bevy_ui crate)
3657 material_label("'ui_material.wgsl' default material"),
3658 (material_node.clone(), MaterialNode(default_material),),
3659 // Custom UI material, 1./3 full.
3660 material_label("'custom_ui_material.wgsl' 1/3"),
3661 (
3662 material_node.clone(),
3663 MaterialNode(ui_materials.add(custom_material(1. / 3.))),
3664 ),
3665 // Custom UI material, 2./3 full.
3666 material_label("'custom_ui_material.wgsl' 2/3"),
3667 (
3668 material_node.clone(),
3669 MaterialNode(ui_materials.add(custom_material(2. / 3.)))
3670 ),
3671 // Custom UI material, full.
3672 material_label("'custom_ui_material.wgsl' Full"),
3673 (material_node.clone(), MaterialNode(full_material.clone()),),
3674 // Custom UI material, full. Clipped using overflow so bottom 50% missing
3675 material_label("'custom_ui_material.wgsl' clipped vertically"),
3676 (
3677 Node {
3678 width: material_node.width,
3679 height: material_node.height,
3680 ..default()
3681 },
3682 Outline {
3683 width: px(1),
3684 color: RED_500.into(),
3685 ..default()
3686 },
3687 children![(
3688 Node {
3689 width: percent(100),
3690 height: percent(50),
3691 overflow: Overflow::clip(),
3692 ..default()
3693 },
3694 children![(material_node.clone(), MaterialNode(full_material.clone()))],
3695 )],
3696 ),
3697 // Custom UI material clipped on both axis so bottom 25% and right 25% not visible.
3698 material_label("'custom_ui_material.wgsl' end clipped on both axis"),
3699 (
3700 Node {
3701 width: material_node.width,
3702 height: material_node.height,
3703 ..default()
3704 },
3705 Outline {
3706 width: px(1),
3707 color: RED_500.into(),
3708 ..default()
3709 },
3710 children![(
3711 Node {
3712 width: percent(75),
3713 height: percent(75),
3714 overflow: Overflow::clip(),
3715 ..default()
3716 },
3717 children![(
3718 Node {
3719 position_type: PositionType::Absolute,
3720 width: px(200),
3721 height: px(80),
3722 border: px(10).all(),
3723 border_radius: BorderRadius::all(px(20)),
3724 ..default()
3725 },
3726 MaterialNode(full_material.clone()),
3727 )],
3728 )],
3729 ),
3730 // Custom UI material clipped on both axis so top 25% and left 25% not visible.
3731 material_label("'custom_ui_material.wgsl' start clipped on both axis"),
3732 (
3733 Node {
3734 width: material_node.width,
3735 height: material_node.height,
3736 align_items: AlignItems::End,
3737 justify_content: JustifyContent::End,
3738 ..default()
3739 },
3740 Outline {
3741 width: px(1),
3742 color: RED_500.into(),
3743 ..default()
3744 },
3745 children![(
3746 Node {
3747 width: percent(75),
3748 height: percent(75),
3749 overflow: Overflow::clip(),
3750 ..default()
3751 },
3752 children![(
3753 Node {
3754 position_type: PositionType::Absolute,
3755 width: px(200),
3756 height: px(80),
3757 right: px(0),
3758 bottom: px(0),
3759 border: px(10).all(),
3760 border_radius: BorderRadius::all(px(20)),
3761 ..default()
3762 },
3763 MaterialNode(full_material),
3764 )],
3765 )],
3766 ),
3767 ],
3768 ));
3769 }More examples
18fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
19 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
20 commands.spawn(Camera2d);
21
22 // Top-level grid (app frame)
23 commands
24 .spawn((
25 Node {
26 // Use the CSS Grid algorithm for laying out this node
27 display: Display::Grid,
28 // Make node fill the entirety of its parent (in this case the window)
29 width: percent(100),
30 height: percent(100),
31 // Set the grid to have 2 columns with sizes [min-content, minmax(0, 1fr)]
32 // - The first column will size to the size of its contents
33 // - The second column will take up the remaining available space
34 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
35 // Set the grid to have 3 rows with sizes [auto, minmax(0, 1fr), 20px]
36 // - The first row will size to the size of its contents
37 // - The second row take up remaining available space (after rows 1 and 3 have both been sized)
38 // - The third row will be exactly 20px high
39 grid_template_rows: vec![
40 GridTrack::auto(),
41 GridTrack::flex(1.0),
42 GridTrack::px(20.),
43 ],
44 ..default()
45 },
46 BackgroundColor(Color::WHITE),
47 ))
48 .with_children(|builder| {
49 // Header
50 builder
51 .spawn(
52 Node {
53 display: Display::Grid,
54 // Make this node span two grid columns so that it takes up the entire top tow
55 grid_column: GridPlacement::span(2),
56 padding: UiRect::all(px(6)),
57 ..default()
58 },
59 )
60 .with_children(|builder| {
61 spawn_nested_text_bundle(builder, font.clone(), "Bevy CSS Grid Layout Example");
62 });
63
64 // Main content grid (auto placed in row 2, column 1)
65 builder
66 .spawn((
67 Node {
68 // Make the height of the node fill its parent
69 height: percent(100),
70 // Make the grid have a 1:1 aspect ratio meaning it will scale as an exact square
71 // As the height is set explicitly, this means the width will adjust to match the height
72 aspect_ratio: Some(1.0),
73 // Use grid layout for this node
74 display: Display::Grid,
75 // Add 24px of padding around the grid
76 padding: UiRect::all(px(24)),
77 // Set the grid to have 4 columns all with sizes minmax(0, 1fr)
78 // This creates 4 exactly evenly sized columns
79 grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
80 // Set the grid to have 4 rows all with sizes minmax(0, 1fr)
81 // This creates 4 exactly evenly sized rows
82 grid_template_rows: RepeatedGridTrack::flex(4, 1.0),
83 // Set a 12px gap/gutter between rows and columns
84 row_gap: px(12),
85 column_gap: px(12),
86 ..default()
87 },
88 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
89 ))
90 .with_children(|builder| {
91 // Note there is no need to specify the position for each grid item. Grid items that are
92 // not given an explicit position will be automatically positioned into the next available
93 // grid cell. The order in which this is performed can be controlled using the grid_auto_flow
94 // style property.
95
96 item_rect(builder, ORANGE);
97 item_rect(builder, BISQUE);
98 item_rect(builder, BLUE);
99 item_rect(builder, CRIMSON);
100 item_rect(builder, AQUA);
101 item_rect(builder, ORANGE_RED);
102 item_rect(builder, DARK_GREEN);
103 item_rect(builder, FUCHSIA);
104 item_rect(builder, TEAL);
105 item_rect(builder, ALICE_BLUE);
106 item_rect(builder, CRIMSON);
107 item_rect(builder, ANTIQUE_WHITE);
108 item_rect(builder, YELLOW);
109 item_rect(builder, DEEP_PINK);
110 item_rect(builder, YELLOW_GREEN);
111 item_rect(builder, SALMON);
112 });
113
114 // Right side bar (auto placed in row 2, column 2)
115 builder
116 .spawn((
117 Node {
118 display: Display::Grid,
119 // Align content towards the start (top) in the vertical axis
120 align_items: AlignItems::Start,
121 // Align content towards the center in the horizontal axis
122 justify_items: JustifyItems::Center,
123 // Add 10px padding
124 padding: UiRect::all(px(10)),
125 // Add an fr track to take up all the available space at the bottom of the column so that the text nodes
126 // can be top-aligned. Normally you'd use flexbox for this, but this is the CSS Grid example so we're using grid.
127 grid_template_rows: vec![GridTrack::auto(), GridTrack::auto(), GridTrack::fr(1.0)],
128 // Add a 10px gap between rows
129 row_gap: px(10),
130 ..default()
131 },
132 BackgroundColor(BLACK.into()),
133 ))
134 .with_children(|builder| {
135 builder.spawn((Text::new("Sidebar"),
136 TextFont::from(font.clone()),
137 ));
138 builder.spawn((Text::new("A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely."),
139 TextFont {
140 font: font.clone().into(),
141 font_size: FontSize::Px(13.0),
142 ..default()
143 },
144 ));
145 builder.spawn(Node::default());
146 });
147
148 // Footer / status bar
149 builder.spawn((
150 Node {
151 // Make this node span two grid column so that it takes up the entire bottom row
152 grid_column: GridPlacement::span(2),
153 ..default()
154 },
155 BackgroundColor(WHITE.into()),
156 ));
157
158 // Modal (absolutely positioned on top of content - currently hidden: to view it, change its visibility)
159 builder.spawn((
160 Node {
161 position_type: PositionType::Absolute,
162 margin: UiRect {
163 top: px(100),
164 bottom: auto(),
165 left: auto(),
166 right: auto(),
167 },
168 width: percent(60),
169 height: px(300),
170 max_width: px(600),
171 ..default()
172 },
173 Visibility::Hidden,
174 BackgroundColor(Color::WHITE.with_alpha(0.8)),
175 ));
176 });
177}39fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
40 commands.spawn(Camera2d);
41
42 let font = TextFont {
43 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
44 font_size: FontSize::Px(24.),
45 ..default()
46 };
47
48 commands
49 .spawn((
50 Node {
51 width: percent(100.),
52 height: percent(100.),
53 display: Display::Grid,
54 justify_content: JustifyContent::Center,
55 align_content: AlignContent::Center,
56 grid_template_columns: vec![GridTrack::px(160.), RepeatedGridTrack::px(3, 320.)],
57 row_gap: px(8.),
58 column_gap: px(8.),
59 ..default()
60 },
61 TabGroup::default(),
62 ))
63 .with_children(|parent| {
64 parent.spawn((
65 Text::new("Multiple Text Inputs Example"),
66 Node {
67 grid_column: GridPlacement::span(4),
68 justify_self: JustifySelf::Center,
69 margin: px(16).bottom(),
70 ..default()
71 },
72 TextColor::WHITE,
73 font.clone(),
74 ));
75
76 let label_font = font.clone().with_font_size(14.);
77 for label in ["Justify", "EditableText", "value", "submission"] {
78 parent.spawn((
79 Text::new(label),
80 label_font.clone(),
81 Node {
82 justify_self: JustifySelf::Center,
83 margin: px(-4).bottom(),
84 ..default()
85 },
86 ));
87 }
88
89 for (row, justify) in [
90 Justify::Left,
91 Justify::Center,
92 Justify::Right,
93 Justify::Justified,
94 Justify::Start,
95 Justify::End,
96 ]
97 .into_iter()
98 .enumerate()
99 {
100 parent.spawn((
101 Node {
102 border: px(4).all(),
103 justify_content: JustifyContent::Center,
104 align_items: AlignItems::Center,
105 ..default()
106 },
107 BorderColor::all(Color::WHITE),
108 children![(Text::new(format!("{justify:?}")), font.clone(),)],
109 ));
110
111 let mut input = parent.spawn((
112 Node {
113 border: px(4.).all(),
114 padding: px(4.).all(),
115 ..default()
116 },
117 TextInput,
118 EditableText::new(format!("Initial text {row}")),
119 TextCursorStyle::default(),
120 font.clone(),
121 BackgroundColor(bevy::color::palettes::css::DARK_GREY.into()),
122 TextInputRow(row),
123 TextLayout::no_wrap().with_justify(justify),
124 TabIndex(row as i32),
125 BorderColor::all(SLATE_300),
126 ));
127 if row == 0 {
128 input.insert(AutoFocus);
129 }
130
131 parent.spawn((
132 Node {
133 border: px(4.).all(),
134 padding: px(4.).all(),
135 overflow: Overflow::clip_x(),
136 overflow_clip_margin: OverflowClipMargin {
137 visual_box: VisualBox::ContentBox,
138 ..default()
139 },
140 ..default()
141 },
142 BackgroundColor(bevy::color::palettes::css::DARK_SLATE_BLUE.into()),
143 BorderColor::all(Color::WHITE),
144 children![(
145 Text::default(),
146 TextLayout::no_wrap(),
147 font.clone(),
148 BackgroundColor(bevy::color::palettes::css::DARK_SLATE_GRAY.into()),
149 BorderColor::all(Color::WHITE),
150 TextInputRow(row),
151 TextOutput,
152 )],
153 ));
154
155 parent.spawn((
156 Node {
157 border: px(4.).all(),
158 padding: px(4.).all(),
159 overflow: Overflow::clip_x(),
160 overflow_clip_margin: OverflowClipMargin {
161 visual_box: VisualBox::ContentBox,
162 ..default()
163 },
164
165 ..default()
166 },
167 BackgroundColor(bevy::color::palettes::css::DARK_SLATE_BLUE.into()),
168 BorderColor::all(Color::WHITE),
169 children![(
170 Text::default(),
171 TextLayout::no_wrap(),
172 font.clone(),
173 TextInputRow(row),
174 SubmitOutput,
175 )],
176 ));
177 }
178
179 let label_font = font.clone().with_font_size(14.);
180 for label in ["ReadWrite", "EditableText", "value", "submission"] {
181 parent.spawn((
182 Text::new(label),
183 label_font.clone(),
184 Node {
185 justify_self: JustifySelf::Center,
186 margin: px(-4).bottom(),
187 ..default()
188 },
189 ));
190 }
191
192 for (row, rwmode) in [
193 TextReadWriteMode::Editable,
194 TextReadWriteMode::ReadOnly,
195 TextReadWriteMode::Static,
196 ]
197 .into_iter()
198 .enumerate()
199 {
200 parent.spawn((
201 Node {
202 border: px(4).all(),
203 justify_content: JustifyContent::Center,
204 align_items: AlignItems::Center,
205 ..default()
206 },
207 BorderColor::all(Color::WHITE),
208 children![(Text::new(format!("{rwmode:?}")), font.clone(),)],
209 ));
210
211 let mut input = parent.spawn((
212 Node {
213 border: px(4.).all(),
214 padding: px(4.).all(),
215 ..default()
216 },
217 TextInput,
218 EditableText::new(format!("Initial text {row}")),
219 TextCursorStyle::default(),
220 font.clone(),
221 BackgroundColor(bevy::color::palettes::css::DARK_GREY.into()),
222 TextInputRow(row + 100),
223 rwmode,
224 TabIndex(row as i32 + 100),
225 BorderColor::all(SLATE_300),
226 ));
227 if row == 0 {
228 input.insert(AutoFocus);
229 }
230
231 parent.spawn((
232 Node {
233 border: px(4.).all(),
234 padding: px(4.).all(),
235 overflow: Overflow::clip_x(),
236 overflow_clip_margin: OverflowClipMargin {
237 visual_box: VisualBox::ContentBox,
238 ..default()
239 },
240 ..default()
241 },
242 BackgroundColor(bevy::color::palettes::css::DARK_SLATE_BLUE.into()),
243 BorderColor::all(Color::WHITE),
244 children![(
245 Text::default(),
246 TextLayout::no_wrap(),
247 font.clone(),
248 BackgroundColor(bevy::color::palettes::css::DARK_SLATE_GRAY.into()),
249 BorderColor::all(Color::WHITE),
250 TextInputRow(row + 100),
251 TextOutput,
252 )],
253 ));
254
255 parent.spawn((
256 Node {
257 border: px(4.).all(),
258 padding: px(4.).all(),
259 overflow: Overflow::clip_x(),
260 overflow_clip_margin: OverflowClipMargin {
261 visual_box: VisualBox::ContentBox,
262 ..default()
263 },
264
265 ..default()
266 },
267 BackgroundColor(bevy::color::palettes::css::DARK_SLATE_BLUE.into()),
268 BorderColor::all(Color::WHITE),
269 children![(
270 Text::default(),
271 TextLayout::no_wrap(),
272 font.clone(),
273 TextInputRow(row + 100),
274 SubmitOutput,
275 )],
276 ));
277 }
278
279 parent.spawn((
280 Text::new("Press Enter to submit"),
281 Node {
282 grid_column: GridPlacement::span(4),
283 justify_self: JustifySelf::Center,
284 margin: px(16).top(),
285 ..default()
286 },
287 font.clone(),
288 ));
289 });
290}Sourcepub fn em<T>(value: f32) -> T
pub fn em<T>(value: f32) -> T
Create a grid track with size as a multiple of the grid container’s font size.
Examples found in repository?
3794 fn text_font_row(font_size: f32) -> impl Bundle {
3795 (
3796 Node {
3797 flex_direction: FlexDirection::Row,
3798 align_items: AlignItems::Center,
3799 column_gap: rem(1),
3800 ..default()
3801 },
3802 children![
3803 (
3804 Node {
3805 width: rem(4),
3806 ..default()
3807 },
3808 Text::new(format!("{font_size}px")),
3809 TextFont::from_font_size(font_size),
3810 TextColor(LAVENDER.into()),
3811 ),
3812 // `em` padding: scales with this node's own font size.
3813 (
3814 Node {
3815 width: rem(20),
3816 justify_content: JustifyContent::Center,
3817 ..default()
3818 },
3819 children![(
3820 Node {
3821 padding: UiRect::all(em(1)),
3822 flex_basis: Val::Auto,
3823 border_radius: em(1).into(),
3824 border: em(0.5).into(),
3825 ..default()
3826 },
3827 EmSize(font_size),
3828 BackgroundColor(DARK_SLATE_BLUE.into()),
3829 BorderColor::all(LAVENDER),
3830 children![(
3831 Text::new("em sizing"),
3832 TextFont::from_font_size(font_size),
3833 BackgroundColor(PEACHPUFF.into()),
3834 TextColor(DARK_SLATE_GRAY.into())
3835 )],
3836 )],
3837 ),
3838 // Always 10rem across
3839 (
3840 Node {
3841 flex_direction: FlexDirection::Row,
3842 align_items: AlignItems::Center,
3843 ..default()
3844 },
3845 EmSize(font_size),
3846 children![(
3847 Node {
3848 width: rem(10),
3849 ..default()
3850 },
3851 Text::new("< rem >"),
3852 TextLayout {
3853 justify: Justify::Center,
3854 ..default()
3855 },
3856 TextFont::from_font_size(font_size),
3857 BackgroundColor(PALE_TURQUOISE.into()),
3858 TextColor(DARK_SLATE_GRAY.into())
3859 )],
3860 ),
3861 // Grid tracks: left is 5em sized, right is 10rem, padding is 0.5rem
3862 (
3863 Node {
3864 width: rem(24),
3865 justify_content: JustifyContent::End,
3866 ..default()
3867 },
3868 children![(
3869 Node {
3870 display: Display::Grid,
3871 grid_template_columns: vec![GridTrack::em(5.), GridTrack::rem(10.)],
3872 padding: px(10).into(),
3873 border_radius: px(10).into(),
3874 border: px(1).into(),
3875 ..default()
3876 },
3877 EmSize(font_size),
3878 BackgroundColor(DARK_SLATE_BLUE.into()),
3879 BorderColor::all(LAVENDER),
3880 children![
3881 (
3882 Node {
3883 display: Display::Grid,
3884 grid_column: GridPlacement::span(2),
3885 ..default()
3886 },
3887 Text::new("px sizing"),
3888 TextFont::from_font_size(px(16)),
3889 TextColor(LAVENDER.into()),
3890 ),
3891 (
3892 Text::new("< em >"),
3893 TextLayout {
3894 justify: Justify::Center,
3895 ..default()
3896 },
3897 TextFont::from_font_size(font_size),
3898 BackgroundColor(PEACHPUFF.into()),
3899 TextColor(DARK_SLATE_GRAY.into())
3900 ),
3901 (
3902 Text::new("< rem >"),
3903 TextLayout {
3904 justify: Justify::Center,
3905 ..default()
3906 },
3907 TextFont::from_font_size(font_size),
3908 BackgroundColor(PALE_TURQUOISE.into()),
3909 TextColor(DARK_SLATE_GRAY.into())
3910 ),
3911 ]
3912 )]
3913 ),
3914 ],
3915 )
3916 }Sourcepub fn rem<T>(value: f32) -> T
pub fn rem<T>(value: f32) -> T
Create a grid track with size as a multiple of the root font size.
Examples found in repository?
3794 fn text_font_row(font_size: f32) -> impl Bundle {
3795 (
3796 Node {
3797 flex_direction: FlexDirection::Row,
3798 align_items: AlignItems::Center,
3799 column_gap: rem(1),
3800 ..default()
3801 },
3802 children![
3803 (
3804 Node {
3805 width: rem(4),
3806 ..default()
3807 },
3808 Text::new(format!("{font_size}px")),
3809 TextFont::from_font_size(font_size),
3810 TextColor(LAVENDER.into()),
3811 ),
3812 // `em` padding: scales with this node's own font size.
3813 (
3814 Node {
3815 width: rem(20),
3816 justify_content: JustifyContent::Center,
3817 ..default()
3818 },
3819 children![(
3820 Node {
3821 padding: UiRect::all(em(1)),
3822 flex_basis: Val::Auto,
3823 border_radius: em(1).into(),
3824 border: em(0.5).into(),
3825 ..default()
3826 },
3827 EmSize(font_size),
3828 BackgroundColor(DARK_SLATE_BLUE.into()),
3829 BorderColor::all(LAVENDER),
3830 children![(
3831 Text::new("em sizing"),
3832 TextFont::from_font_size(font_size),
3833 BackgroundColor(PEACHPUFF.into()),
3834 TextColor(DARK_SLATE_GRAY.into())
3835 )],
3836 )],
3837 ),
3838 // Always 10rem across
3839 (
3840 Node {
3841 flex_direction: FlexDirection::Row,
3842 align_items: AlignItems::Center,
3843 ..default()
3844 },
3845 EmSize(font_size),
3846 children![(
3847 Node {
3848 width: rem(10),
3849 ..default()
3850 },
3851 Text::new("< rem >"),
3852 TextLayout {
3853 justify: Justify::Center,
3854 ..default()
3855 },
3856 TextFont::from_font_size(font_size),
3857 BackgroundColor(PALE_TURQUOISE.into()),
3858 TextColor(DARK_SLATE_GRAY.into())
3859 )],
3860 ),
3861 // Grid tracks: left is 5em sized, right is 10rem, padding is 0.5rem
3862 (
3863 Node {
3864 width: rem(24),
3865 justify_content: JustifyContent::End,
3866 ..default()
3867 },
3868 children![(
3869 Node {
3870 display: Display::Grid,
3871 grid_template_columns: vec![GridTrack::em(5.), GridTrack::rem(10.)],
3872 padding: px(10).into(),
3873 border_radius: px(10).into(),
3874 border: px(1).into(),
3875 ..default()
3876 },
3877 EmSize(font_size),
3878 BackgroundColor(DARK_SLATE_BLUE.into()),
3879 BorderColor::all(LAVENDER),
3880 children![
3881 (
3882 Node {
3883 display: Display::Grid,
3884 grid_column: GridPlacement::span(2),
3885 ..default()
3886 },
3887 Text::new("px sizing"),
3888 TextFont::from_font_size(px(16)),
3889 TextColor(LAVENDER.into()),
3890 ),
3891 (
3892 Text::new("< em >"),
3893 TextLayout {
3894 justify: Justify::Center,
3895 ..default()
3896 },
3897 TextFont::from_font_size(font_size),
3898 BackgroundColor(PEACHPUFF.into()),
3899 TextColor(DARK_SLATE_GRAY.into())
3900 ),
3901 (
3902 Text::new("< rem >"),
3903 TextLayout {
3904 justify: Justify::Center,
3905 ..default()
3906 },
3907 TextFont::from_font_size(font_size),
3908 BackgroundColor(PALE_TURQUOISE.into()),
3909 TextColor(DARK_SLATE_GRAY.into())
3910 ),
3911 ]
3912 )]
3913 ),
3914 ],
3915 )
3916 }Sourcepub fn fr<T>(value: f32) -> T
pub fn fr<T>(value: f32) -> T
Create a grid track with an fr size.
Note that this will give the track a content-based minimum size.
Usually you are best off using GridTrack::flex instead which uses a zero minimum size.
Examples found in repository?
18fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
19 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
20 commands.spawn(Camera2d);
21
22 // Top-level grid (app frame)
23 commands
24 .spawn((
25 Node {
26 // Use the CSS Grid algorithm for laying out this node
27 display: Display::Grid,
28 // Make node fill the entirety of its parent (in this case the window)
29 width: percent(100),
30 height: percent(100),
31 // Set the grid to have 2 columns with sizes [min-content, minmax(0, 1fr)]
32 // - The first column will size to the size of its contents
33 // - The second column will take up the remaining available space
34 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
35 // Set the grid to have 3 rows with sizes [auto, minmax(0, 1fr), 20px]
36 // - The first row will size to the size of its contents
37 // - The second row take up remaining available space (after rows 1 and 3 have both been sized)
38 // - The third row will be exactly 20px high
39 grid_template_rows: vec![
40 GridTrack::auto(),
41 GridTrack::flex(1.0),
42 GridTrack::px(20.),
43 ],
44 ..default()
45 },
46 BackgroundColor(Color::WHITE),
47 ))
48 .with_children(|builder| {
49 // Header
50 builder
51 .spawn(
52 Node {
53 display: Display::Grid,
54 // Make this node span two grid columns so that it takes up the entire top tow
55 grid_column: GridPlacement::span(2),
56 padding: UiRect::all(px(6)),
57 ..default()
58 },
59 )
60 .with_children(|builder| {
61 spawn_nested_text_bundle(builder, font.clone(), "Bevy CSS Grid Layout Example");
62 });
63
64 // Main content grid (auto placed in row 2, column 1)
65 builder
66 .spawn((
67 Node {
68 // Make the height of the node fill its parent
69 height: percent(100),
70 // Make the grid have a 1:1 aspect ratio meaning it will scale as an exact square
71 // As the height is set explicitly, this means the width will adjust to match the height
72 aspect_ratio: Some(1.0),
73 // Use grid layout for this node
74 display: Display::Grid,
75 // Add 24px of padding around the grid
76 padding: UiRect::all(px(24)),
77 // Set the grid to have 4 columns all with sizes minmax(0, 1fr)
78 // This creates 4 exactly evenly sized columns
79 grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
80 // Set the grid to have 4 rows all with sizes minmax(0, 1fr)
81 // This creates 4 exactly evenly sized rows
82 grid_template_rows: RepeatedGridTrack::flex(4, 1.0),
83 // Set a 12px gap/gutter between rows and columns
84 row_gap: px(12),
85 column_gap: px(12),
86 ..default()
87 },
88 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
89 ))
90 .with_children(|builder| {
91 // Note there is no need to specify the position for each grid item. Grid items that are
92 // not given an explicit position will be automatically positioned into the next available
93 // grid cell. The order in which this is performed can be controlled using the grid_auto_flow
94 // style property.
95
96 item_rect(builder, ORANGE);
97 item_rect(builder, BISQUE);
98 item_rect(builder, BLUE);
99 item_rect(builder, CRIMSON);
100 item_rect(builder, AQUA);
101 item_rect(builder, ORANGE_RED);
102 item_rect(builder, DARK_GREEN);
103 item_rect(builder, FUCHSIA);
104 item_rect(builder, TEAL);
105 item_rect(builder, ALICE_BLUE);
106 item_rect(builder, CRIMSON);
107 item_rect(builder, ANTIQUE_WHITE);
108 item_rect(builder, YELLOW);
109 item_rect(builder, DEEP_PINK);
110 item_rect(builder, YELLOW_GREEN);
111 item_rect(builder, SALMON);
112 });
113
114 // Right side bar (auto placed in row 2, column 2)
115 builder
116 .spawn((
117 Node {
118 display: Display::Grid,
119 // Align content towards the start (top) in the vertical axis
120 align_items: AlignItems::Start,
121 // Align content towards the center in the horizontal axis
122 justify_items: JustifyItems::Center,
123 // Add 10px padding
124 padding: UiRect::all(px(10)),
125 // Add an fr track to take up all the available space at the bottom of the column so that the text nodes
126 // can be top-aligned. Normally you'd use flexbox for this, but this is the CSS Grid example so we're using grid.
127 grid_template_rows: vec![GridTrack::auto(), GridTrack::auto(), GridTrack::fr(1.0)],
128 // Add a 10px gap between rows
129 row_gap: px(10),
130 ..default()
131 },
132 BackgroundColor(BLACK.into()),
133 ))
134 .with_children(|builder| {
135 builder.spawn((Text::new("Sidebar"),
136 TextFont::from(font.clone()),
137 ));
138 builder.spawn((Text::new("A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely."),
139 TextFont {
140 font: font.clone().into(),
141 font_size: FontSize::Px(13.0),
142 ..default()
143 },
144 ));
145 builder.spawn(Node::default());
146 });
147
148 // Footer / status bar
149 builder.spawn((
150 Node {
151 // Make this node span two grid column so that it takes up the entire bottom row
152 grid_column: GridPlacement::span(2),
153 ..default()
154 },
155 BackgroundColor(WHITE.into()),
156 ));
157
158 // Modal (absolutely positioned on top of content - currently hidden: to view it, change its visibility)
159 builder.spawn((
160 Node {
161 position_type: PositionType::Absolute,
162 margin: UiRect {
163 top: px(100),
164 bottom: auto(),
165 left: auto(),
166 right: auto(),
167 },
168 width: percent(60),
169 height: px(300),
170 max_width: px(600),
171 ..default()
172 },
173 Visibility::Hidden,
174 BackgroundColor(Color::WHITE.with_alpha(0.8)),
175 ));
176 });
177}Sourcepub fn flex<T>(value: f32) -> T
pub fn flex<T>(value: f32) -> T
Create a grid track with a minmax(0, Nfr) size.
Examples found in repository?
16fn setup(mut commands: Commands, mut font_system: ResMut<FontCx>) {
17 let mut families: Vec<String> = font_system
18 .context
19 .collection
20 .family_names()
21 .map(ToOwned::to_owned)
22 .collect();
23 families.sort_unstable();
24 families.dedup();
25 let family_count = families.len();
26
27 commands.spawn(Camera2d);
28
29 commands
30 .spawn((
31 Node {
32 flex_direction: FlexDirection::Column,
33 width: percent(100),
34 height: percent(100),
35 align_items: AlignItems::Center,
36 row_gap: px(10.),
37 ..default()
38 },
39 BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
40 ))
41 .with_children(move |builder| {
42 builder.spawn(Text::new(format!(
43 "Total available fonts: {}",
44 family_count,
45 )));
46
47 builder
48 .spawn(Node {
49 flex_direction: FlexDirection::Column,
50 row_gap: px(6),
51 overflow: Overflow::scroll_y(),
52 align_items: AlignItems::Stretch,
53 ..default()
54 })
55 .with_children(|builder| {
56 for family in families {
57 let font = FontSource::Family(family.clone().into());
58 builder.spawn((
59 Node {
60 display: Display::Grid,
61 grid_template_columns: vec![
62 GridTrack::flex(1.),
63 GridTrack::flex(1.),
64 ],
65 padding: px(6).all(),
66 column_gap: px(50.),
67 ..default()
68 },
69 BackgroundColor(Color::srgb(0.2, 0.2, 0.25)),
70 children![
71 (
72 Text::new(&family),
73 TextFont { font, ..default() },
74 TextLayout::no_wrap()
75 ),
76 (Text::new(family), TextLayout::no_wrap()),
77 ],
78 ));
79 }
80 })
81 .observe(
82 |on_scroll: On<PointerScroll>,
83 mut query: Query<(&mut ScrollPosition, &ComputedNode)>| {
84 if let Ok((mut scroll_position, node)) = query.get_mut(on_scroll.entity) {
85 let dy = match on_scroll.unit {
86 MouseScrollUnit::Line => on_scroll.y * 20.,
87 MouseScrollUnit::Pixel => on_scroll.y,
88 };
89 let range = (node.content_size.y - node.size.y).max(0.)
90 * node.inverse_scale_factor;
91 scroll_position.y = (scroll_position.y - dy).clamp(0., range);
92 }
93 },
94 );
95 });
96}More examples
62fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
63 let image_handle = asset_server.load("branding/icon.png");
64 let full_text = format!(
65 "{}height : {}%, width : {}%",
66 TEXT_PREFIX, IMAGE_GROUP_BOX_INIT_HEIGHT, IMAGE_GROUP_BOX_INIT_WIDTH,
67 );
68
69 commands.spawn(Camera2d);
70
71 let container = commands
72 .spawn((
73 Node {
74 display: Display::Grid,
75 width: percent(100),
76 height: percent(100),
77 grid_template_rows: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
78 ..default()
79 },
80 BackgroundColor(Color::WHITE),
81 ))
82 .id();
83
84 // Keyboard Text
85 commands
86 .spawn((
87 TextData {
88 height: IMAGE_GROUP_BOX_INIT_HEIGHT,
89 width: IMAGE_GROUP_BOX_INIT_WIDTH,
90 },
91 Text::new(full_text),
92 TextColor::BLACK,
93 Node {
94 grid_row: GridPlacement::span(1),
95 padding: px(6).all(),
96 ..default()
97 },
98 UiDebugOptions {
99 enabled: false,
100 ..default()
101 },
102 ChildOf(container),
103 ))
104 .observe(update_text);
105
106 commands
107 .spawn((
108 Node {
109 display: Display::Flex,
110 grid_row: GridPlacement::span(1),
111 flex_direction: FlexDirection::Column,
112 justify_content: JustifyContent::SpaceAround,
113 padding: px(10.).all(),
114 ..default()
115 },
116 BackgroundColor(Color::BLACK),
117 ChildOf(container),
118 ))
119 .with_children(|builder| {
120 // `NodeImageMode::Auto` will resize the image automatically by taking the size of the source image and applying any layout constraints.
121 builder
122 .spawn((
123 ImageGroup,
124 Node {
125 display: Display::Flex,
126 justify_content: JustifyContent::Start,
127 width: percent(IMAGE_GROUP_BOX_INIT_WIDTH),
128 height: percent(IMAGE_GROUP_BOX_INIT_HEIGHT),
129 ..default()
130 },
131 BackgroundColor(Color::from(tailwind::BLUE_100)),
132 ))
133 .with_children(|parent| {
134 for _ in 0..4 {
135 // child node will apply Flex layout
136 parent.spawn((
137 Node::default(),
138 ImageNode {
139 image: image_handle.clone(),
140 image_mode: NodeImageMode::Auto,
141 ..default()
142 },
143 ));
144 }
145 });
146 // `NodeImageMode::Stretch` will resize the image to match the size of the `Node` component
147 builder
148 .spawn((
149 ImageGroup,
150 Node {
151 display: Display::Flex,
152 justify_content: JustifyContent::Start,
153 width: percent(IMAGE_GROUP_BOX_INIT_WIDTH),
154 height: percent(IMAGE_GROUP_BOX_INIT_HEIGHT),
155 ..default()
156 },
157 BackgroundColor(Color::from(tailwind::BLUE_100)),
158 ))
159 .with_children(|parent| {
160 for width in [10., 20., 30., 40.] {
161 parent.spawn((
162 Node {
163 height: percent(100),
164 width: percent(width),
165 ..default()
166 },
167 ImageNode {
168 image: image_handle.clone(),
169 image_mode: NodeImageMode::Stretch,
170 ..default()
171 },
172 ));
173 }
174 });
175 });
176}18fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
19 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
20 commands.spawn(Camera2d);
21
22 // Top-level grid (app frame)
23 commands
24 .spawn((
25 Node {
26 // Use the CSS Grid algorithm for laying out this node
27 display: Display::Grid,
28 // Make node fill the entirety of its parent (in this case the window)
29 width: percent(100),
30 height: percent(100),
31 // Set the grid to have 2 columns with sizes [min-content, minmax(0, 1fr)]
32 // - The first column will size to the size of its contents
33 // - The second column will take up the remaining available space
34 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
35 // Set the grid to have 3 rows with sizes [auto, minmax(0, 1fr), 20px]
36 // - The first row will size to the size of its contents
37 // - The second row take up remaining available space (after rows 1 and 3 have both been sized)
38 // - The third row will be exactly 20px high
39 grid_template_rows: vec![
40 GridTrack::auto(),
41 GridTrack::flex(1.0),
42 GridTrack::px(20.),
43 ],
44 ..default()
45 },
46 BackgroundColor(Color::WHITE),
47 ))
48 .with_children(|builder| {
49 // Header
50 builder
51 .spawn(
52 Node {
53 display: Display::Grid,
54 // Make this node span two grid columns so that it takes up the entire top tow
55 grid_column: GridPlacement::span(2),
56 padding: UiRect::all(px(6)),
57 ..default()
58 },
59 )
60 .with_children(|builder| {
61 spawn_nested_text_bundle(builder, font.clone(), "Bevy CSS Grid Layout Example");
62 });
63
64 // Main content grid (auto placed in row 2, column 1)
65 builder
66 .spawn((
67 Node {
68 // Make the height of the node fill its parent
69 height: percent(100),
70 // Make the grid have a 1:1 aspect ratio meaning it will scale as an exact square
71 // As the height is set explicitly, this means the width will adjust to match the height
72 aspect_ratio: Some(1.0),
73 // Use grid layout for this node
74 display: Display::Grid,
75 // Add 24px of padding around the grid
76 padding: UiRect::all(px(24)),
77 // Set the grid to have 4 columns all with sizes minmax(0, 1fr)
78 // This creates 4 exactly evenly sized columns
79 grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
80 // Set the grid to have 4 rows all with sizes minmax(0, 1fr)
81 // This creates 4 exactly evenly sized rows
82 grid_template_rows: RepeatedGridTrack::flex(4, 1.0),
83 // Set a 12px gap/gutter between rows and columns
84 row_gap: px(12),
85 column_gap: px(12),
86 ..default()
87 },
88 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
89 ))
90 .with_children(|builder| {
91 // Note there is no need to specify the position for each grid item. Grid items that are
92 // not given an explicit position will be automatically positioned into the next available
93 // grid cell. The order in which this is performed can be controlled using the grid_auto_flow
94 // style property.
95
96 item_rect(builder, ORANGE);
97 item_rect(builder, BISQUE);
98 item_rect(builder, BLUE);
99 item_rect(builder, CRIMSON);
100 item_rect(builder, AQUA);
101 item_rect(builder, ORANGE_RED);
102 item_rect(builder, DARK_GREEN);
103 item_rect(builder, FUCHSIA);
104 item_rect(builder, TEAL);
105 item_rect(builder, ALICE_BLUE);
106 item_rect(builder, CRIMSON);
107 item_rect(builder, ANTIQUE_WHITE);
108 item_rect(builder, YELLOW);
109 item_rect(builder, DEEP_PINK);
110 item_rect(builder, YELLOW_GREEN);
111 item_rect(builder, SALMON);
112 });
113
114 // Right side bar (auto placed in row 2, column 2)
115 builder
116 .spawn((
117 Node {
118 display: Display::Grid,
119 // Align content towards the start (top) in the vertical axis
120 align_items: AlignItems::Start,
121 // Align content towards the center in the horizontal axis
122 justify_items: JustifyItems::Center,
123 // Add 10px padding
124 padding: UiRect::all(px(10)),
125 // Add an fr track to take up all the available space at the bottom of the column so that the text nodes
126 // can be top-aligned. Normally you'd use flexbox for this, but this is the CSS Grid example so we're using grid.
127 grid_template_rows: vec![GridTrack::auto(), GridTrack::auto(), GridTrack::fr(1.0)],
128 // Add a 10px gap between rows
129 row_gap: px(10),
130 ..default()
131 },
132 BackgroundColor(BLACK.into()),
133 ))
134 .with_children(|builder| {
135 builder.spawn((Text::new("Sidebar"),
136 TextFont::from(font.clone()),
137 ));
138 builder.spawn((Text::new("A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely."),
139 TextFont {
140 font: font.clone().into(),
141 font_size: FontSize::Px(13.0),
142 ..default()
143 },
144 ));
145 builder.spawn(Node::default());
146 });
147
148 // Footer / status bar
149 builder.spawn((
150 Node {
151 // Make this node span two grid column so that it takes up the entire bottom row
152 grid_column: GridPlacement::span(2),
153 ..default()
154 },
155 BackgroundColor(WHITE.into()),
156 ));
157
158 // Modal (absolutely positioned on top of content - currently hidden: to view it, change its visibility)
159 builder.spawn((
160 Node {
161 position_type: PositionType::Absolute,
162 margin: UiRect {
163 top: px(100),
164 bottom: auto(),
165 left: auto(),
166 right: auto(),
167 },
168 width: percent(60),
169 height: px(300),
170 max_width: px(600),
171 ..default()
172 },
173 Visibility::Hidden,
174 BackgroundColor(Color::WHITE.with_alpha(0.8)),
175 ));
176 });
177}382 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
383 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Text)));
384
385 let mut container = commands.spawn((
386 Node {
387 flex_direction: FlexDirection::Column,
388 ..default()
389 },
390 DespawnOnExit(super::Scene::Text),
391 ));
392
393 container.with_child((
394 Text::new("Hello World."),
395 TextFont {
396 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
397 font_size: FontSize::Px(100.),
398 ..default()
399 },
400 ));
401
402 container.with_children(|builder| {
403 let mut grid = builder.spawn(Node {
404 display: Display::Grid,
405 grid_template_columns: vec![GridTrack::flex(1.0), GridTrack::flex(1.0)],
406 padding: UiRect::horizontal(px(5.)),
407 ..default()
408 });
409
410 grid.with_children(|grid| {
411 for hinting in [FontHinting::Enabled, FontHinting::Disabled] {
412 let mut content = grid.spawn(Node {
413 flex_direction: FlexDirection::Column,
414 row_gap: px(5.),
415 ..default()
416 });
417
418 content.with_child((
419 Text::new(format!("FontHinting::{:?}", hinting)),
420 TextFont {
421 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
422 ..default()
423 },
424 hinting,
425 ));
426
427 content.with_child((
428 Text::new("Font from css font list"),
429 TextFont {
430 font: FontSource::families(
431 "'Comic Sans', Arial, 'Noto Sans', sans-serif",
432 ),
433 ..Default::default()
434 },
435 ));
436
437 content.with_child((
438 Text::new("white "),
439 TextFont {
440 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
441 ..default()
442 },
443 hinting,
444 children![
445 (TextSpan::new("red "), TextColor(RED.into()),),
446 (TextSpan::new("green "), TextColor(GREEN.into()),),
447 (TextSpan::new("blue "), TextColor(BLUE.into()),),
448 (
449 TextSpan::new("black"),
450 TextColor(Color::BLACK),
451 TextFont {
452 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
453 ..default()
454 },
455 TextBackgroundColor(Color::WHITE)
456 ),
457 ],
458 ));
459
460 content.with_child((
461 Text::new(""),
462 TextFont {
463 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
464 ..default()
465 },
466 hinting,
467 children![
468 (
469 TextSpan::new("white "),
470 TextFont {
471 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
472 ..default()
473 }
474 ),
475 (TextSpan::new("red "), TextColor(RED.into()),),
476 (TextSpan::new("green "), TextColor(GREEN.into()),),
477 (TextSpan::new("blue "), TextColor(BLUE.into()),),
478 (
479 TextSpan::new("black"),
480 TextColor(Color::BLACK),
481 TextFont {
482 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
483 ..default()
484 },
485 TextBackgroundColor(Color::WHITE)
486 ),
487 ],
488 ));
489
490 content.with_child((
491 Text::new(""),
492 TextFont {
493 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
494 ..default()
495 },
496 hinting,
497 children![
498 (TextSpan::new(""), TextColor(YELLOW.into()),),
499 TextSpan::new(""),
500 (
501 TextSpan::new("white "),
502 TextFont {
503 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
504 ..default()
505 }
506 ),
507 TextSpan::new(""),
508 (TextSpan::new("red "), TextColor(RED.into()),),
509 TextSpan::new(""),
510 TextSpan::new(""),
511 (TextSpan::new("green "), TextColor(GREEN.into()),),
512 (TextSpan::new(""), TextColor(YELLOW.into()),),
513 (TextSpan::new("blue "), TextColor(BLUE.into()),),
514 TextSpan::new(""),
515 (TextSpan::new(""), TextColor(YELLOW.into()),),
516 (
517 TextSpan::new("black"),
518 TextColor(Color::BLACK),
519 TextFont {
520 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
521 ..default()
522 },
523 TextBackgroundColor(Color::WHITE)
524 ),
525 TextSpan::new(""),
526 ],
527 ));
528
529 content.with_child((
530 hinting,
531 Text::new("FiraSans_"),
532 TextFont {
533 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
534 font_size: FontSize::Px(25.),
535 ..default()
536 },
537 children![
538 (
539 TextSpan::new("MonaSans_"),
540 TextFont {
541 font: asset_server
542 .load("fonts/MonaSans-VariableFont.ttf")
543 .into(),
544 font_size: FontSize::Px(25.),
545 ..default()
546 }
547 ),
548 (
549 TextSpan::new("EBGaramond_"),
550 TextFont {
551 font: asset_server
552 .load("fonts/EBGaramond12-Regular.otf")
553 .into(),
554 font_size: FontSize::Px(25.),
555 ..default()
556 },
557 ),
558 (
559 TextSpan::new("FiraMono"),
560 TextFont {
561 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
562 font_size: FontSize::Px(25.),
563 ..default()
564 },
565 ),
566 ],
567 ));
568
569 content.with_child((
570 hinting,
571 Text::new("FiraSans "),
572 TextFont {
573 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
574 font_size: FontSize::Px(25.),
575 ..default()
576 },
577 children![
578 (
579 TextSpan::new("MonaSans "),
580 TextFont {
581 font: asset_server
582 .load("fonts/MonaSans-VariableFont.ttf")
583 .into(),
584 font_size: FontSize::Px(25.),
585 ..default()
586 }
587 ),
588 (
589 TextSpan::new("EBGaramond "),
590 TextFont {
591 font: asset_server
592 .load("fonts/EBGaramond12-Regular.otf")
593 .into(),
594 font_size: FontSize::Px(25.),
595 ..default()
596 },
597 ),
598 (
599 TextSpan::new("FiraMono"),
600 TextFont {
601 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
602 font_size: FontSize::Px(25.),
603 ..default()
604 },
605 ),
606 ],
607 ));
608
609 content.with_child((
610 hinting,
611 Text::new("FiraSans "),
612 TextFont {
613 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
614 font_size: FontSize::Px(25.),
615 ..default()
616 },
617 children![
618 (
619 TextSpan::new("MonaSans_"),
620 TextFont {
621 font: asset_server
622 .load("fonts/MonaSans-VariableFont.ttf")
623 .into(),
624 font_size: FontSize::Px(25.),
625 ..default()
626 }
627 ),
628 (
629 TextSpan::new("EBGaramond "),
630 TextFont {
631 font: asset_server
632 .load("fonts/EBGaramond12-Regular.otf")
633 .into(),
634 font_size: FontSize::Px(25.),
635 ..default()
636 },
637 ),
638 (
639 TextSpan::new("FiraMono"),
640 TextFont {
641 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
642 font_size: FontSize::Px(25.),
643 ..default()
644 },
645 ),
646 ],
647 ));
648
649 content.with_child((
650 hinting,
651 Text::new("FiraSans"),
652 TextFont {
653 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
654 font_size: FontSize::Px(25.),
655 ..default()
656 },
657 children![
658 TextSpan::new(" "),
659 (
660 TextSpan::new("MonaSans"),
661 TextFont {
662 font: asset_server
663 .load("fonts/MonaSans-VariableFont.ttf")
664 .into(),
665 font_size: FontSize::Px(25.),
666 ..default()
667 }
668 ),
669 TextSpan::new(" "),
670 (
671 TextSpan::new("EBGaramond"),
672 TextFont {
673 font: asset_server
674 .load("fonts/EBGaramond12-Regular.otf")
675 .into(),
676 font_size: FontSize::Px(25.),
677 ..default()
678 },
679 ),
680 TextSpan::new(" "),
681 (
682 TextSpan::new("FiraMono"),
683 TextFont {
684 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
685 font_size: FontSize::Px(25.),
686 ..default()
687 },
688 ),
689 ],
690 ));
691
692 content.with_child((
693 hinting,
694 Text::new("Fira Sans_"),
695 TextFont {
696 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
697 font_size: FontSize::Px(25.),
698 ..default()
699 },
700 children![
701 (
702 TextSpan::new("Mona Sans_"),
703 TextFont {
704 font: asset_server
705 .load("fonts/MonaSans-VariableFont.ttf")
706 .into(),
707 font_size: FontSize::Px(25.),
708 ..default()
709 }
710 ),
711 (
712 TextSpan::new("EB Garamond_"),
713 TextFont {
714 font: asset_server
715 .load("fonts/EBGaramond12-Regular.otf")
716 .into(),
717 font_size: FontSize::Px(25.),
718 ..default()
719 },
720 ),
721 (
722 TextSpan::new("Fira Mono"),
723 TextFont {
724 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
725 font_size: FontSize::Px(25.),
726 ..default()
727 },
728 ),
729 ],
730 ));
731
732 content.with_child((
733 hinting,
734 Text::new("FontWeight(100)_"),
735 TextFont {
736 font: "Mona Sans".into(),
737 font_size: FontSize::Px(25.),
738 weight: FontWeight(100),
739 ..default()
740 },
741 children![
742 (
743 TextSpan::new("FontWeight(500)_"),
744 TextFont {
745 font: "Mona Sans".into(),
746 font_size: FontSize::Px(25.),
747 weight: FontWeight(500),
748 ..default()
749 }
750 ),
751 (
752 TextSpan::new("FontWeight(900)"),
753 TextFont {
754 font: "Mona Sans".into(),
755 font_size: FontSize::Px(25.),
756 weight: FontWeight(900),
757 ..default()
758 },
759 ),
760 ],
761 ));
762
763 content.with_child((
764 hinting,
765 Text::new("FiraSans_"),
766 TextFont {
767 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
768 font_size: FontSize::Px(25.),
769 weight: FontWeight(900),
770 ..default()
771 },
772 children![
773 (
774 TextSpan::new("MonaSans_"),
775 TextFont {
776 font: asset_server
777 .load("fonts/MonaSans-VariableFont.ttf")
778 .into(),
779 font_size: FontSize::Px(25.),
780 weight: FontWeight(700),
781 ..default()
782 }
783 ),
784 (
785 TextSpan::new("EBGaramond_"),
786 TextFont {
787 font: asset_server
788 .load("fonts/EBGaramond12-Regular.otf")
789 .into(),
790 font_size: FontSize::Px(25.),
791 weight: FontWeight(500),
792 ..default()
793 },
794 ),
795 (
796 TextSpan::new("FiraMono"),
797 TextFont {
798 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
799 font_size: FontSize::Px(25.),
800 weight: FontWeight(300),
801 ..default()
802 },
803 ),
804 ],
805 ));
806
807 content.with_child((
808 hinting,
809 Text::new("FiraSans\t"),
810 TextFont {
811 font: asset_server.load("fonts/FiraSans-Bold.ttf").into(),
812 font_size: FontSize::Px(25.),
813 ..default()
814 },
815 children![
816 (
817 TextSpan::new("MonaSans\t"),
818 TextFont {
819 font: asset_server
820 .load("fonts/MonaSans-VariableFont.ttf")
821 .into(),
822 font_size: FontSize::Px(25.),
823 ..default()
824 }
825 ),
826 (
827 TextSpan::new("EBGaramond\t"),
828 TextFont {
829 font: asset_server
830 .load("fonts/EBGaramond12-Regular.otf")
831 .into(),
832 font_size: FontSize::Px(25.),
833 ..default()
834 },
835 ),
836 (
837 TextSpan::new("FiraMono"),
838 TextFont {
839 font: asset_server.load("fonts/FiraMono-Medium.ttf").into(),
840 font_size: FontSize::Px(25.),
841 ..default()
842 },
843 ),
844 ],
845 ));
846
847 for font_smoothing in [FontSmoothing::AntiAliased, FontSmoothing::None] {
848 content.with_child((
849 Text::new(format!("FontSmoothing::{:?}", font_smoothing)),
850 TextFont {
851 font: asset_server.load("fonts/MonaSans-VariableFont.ttf").into(),
852 font_size: FontSize::Px(25.),
853 font_smoothing,
854 ..default()
855 },
856 ));
857 }
858 }
859 });
860 });
861 }
862}
863
864mod font_lists {
865 use bevy::prelude::*;
866
867 const FONT_ASSETS: &[&str] = &[
868 "fonts/FiraSans-Bold.ttf",
869 "fonts/FiraMono-Medium.ttf",
870 "fonts/MonaSans-VariableFont.ttf",
871 "fonts/EBGaramond12-Regular.otf",
872 ];
873
874 const FONT_NAMES: &[&str] = &[
875 "Gabriola",
876 "Fira Sans",
877 "Fira Mono",
878 "Mona Sans",
879 "EB Garamond",
880 ];
881
882 #[derive(Resource)]
883 struct LoadedFontAssets {
884 _handles: Vec<Handle<Font>>,
885 }
886
887 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
888 commands.spawn((Camera2d, DespawnOnExit(super::Scene::FontLists)));
889 commands.insert_resource(LoadedFontAssets {
890 _handles: FONT_ASSETS
891 .iter()
892 .map(|font_asset| asset_server.load(*font_asset))
893 .collect(),
894 });
895 commands.spawn((
896 Node {
897 flex_direction: FlexDirection::Column,
898 align_self: AlignSelf::Center,
899 justify_self: JustifySelf::Center,
900 row_gap: px(25),
901 ..default()
902 },
903 DespawnOnExit(super::Scene::FontLists),
904 children![
905 (
906 Text::new("Font Lists"),
907 TextFont::from_font_size(FontSize::Px(32.)),
908 Underline,
909 ),
910 (
911 Node {
912 flex_direction: FlexDirection::Column,
913 row_gap: px(6),
914 ..default()
915 },
916 children![
917 Text::new("FontSource::Families"),
918 (
919 Node {
920 flex_direction: FlexDirection::Row,
921 flex_wrap: FlexWrap::Wrap,
922 padding: px(16).left(),
923 column_gap: px(30),
924 row_gap: px(30),
925 ..default()
926 },
927 Children::spawn(SpawnIter(
928 (0..FONT_NAMES.len())
929 .map(|start| {
930 FONT_NAMES
931 .iter()
932 .copied()
933 .cycle()
934 .skip(start)
935 .take(FONT_NAMES.len())
936 .collect::<Vec<_>>()
937 .join(", ")
938 })
939 .map(|list| {
940 (
941 Text::new(list.replace(", ", "\n")),
942 TextFont {
943 font: FontSource::families(list),
944 font_size: FontSize::Px(16.),
945 ..default()
946 },
947 Node {
948 padding: px(4.).all(),
949 ..default()
950 },
951 TextLayout::no_wrap(),
952 Outline::default(),
953 )
954 }),
955 )),
956 )
957 ]
958 ),
959 (
960 Node {
961 flex_direction: FlexDirection::Column,
962 row_gap: px(6),
963 ..default()
964 },
965 children![
966 Text::new("FontSource::List"),
967 (
968 Node {
969 flex_direction: FlexDirection::Row,
970 flex_wrap: FlexWrap::Wrap,
971 padding: px(16).left(),
972 column_gap: px(30),
973 row_gap: px(30),
974 ..default()
975 },
976 Children::spawn(SpawnIter(
977 (0..FONT_NAMES.len())
978 .map(|start| {
979 FONT_NAMES
980 .iter()
981 .copied()
982 .cycle()
983 .skip(start)
984 .take(FONT_NAMES.len())
985 .collect::<Vec<_>>()
986 })
987 .map(|list| {
988 (
989 Text::new(list.join("\n")),
990 TextFont {
991 font: FontSource::list(list.iter().copied()),
992 font_size: FontSize::Px(16.),
993 ..default()
994 },
995 Node {
996 padding: px(4.).all(),
997 ..default()
998 },
999 TextLayout::no_wrap(),
1000 Outline::default(),
1001 )
1002 }),
1003 )),
1004 )
1005 ]
1006 ),
1007 ],
1008 ));
1009 }
1010}
1011mod text_measurement {
1012 use bevy::prelude::*;
1013
1014 pub fn setup(mut commands: Commands) {
1015 commands.spawn((Camera2d, DespawnOnExit(super::Scene::TextMeasurement)));
1016
1017 commands
1018 .spawn((
1019 Node {
1020 width: percent(100),
1021 height: percent(100),
1022 flex_direction: FlexDirection::Row,
1023 column_gap: px(8),
1024 padding: px(8).horizontal(),
1025 ..default()
1026 },
1027 DespawnOnExit(super::Scene::TextMeasurement),
1028 ))
1029 .with_children(|parent| {
1030 let width = px(102);
1031 for (flex_direction, boxed) in [
1032 (FlexDirection::Row, true),
1033 (FlexDirection::Row, false),
1034 (FlexDirection::Column, true),
1035 (FlexDirection::Column, false),
1036 ] {
1037 parent
1038 .spawn(Node {
1039 flex_direction: FlexDirection::Column,
1040 ..default()
1041 })
1042 .with_children(|parent| {
1043 for align_items in [
1044 AlignItems::Baseline,
1045 AlignItems::Center,
1046 AlignItems::Stretch,
1047 AlignItems::FlexStart,
1048 AlignItems::FlexEnd,
1049 ] {
1050 parent.spawn((
1051 Node {
1052 margin: px(8).top(),
1053 ..default()
1054 },
1055 Text::new(format!("AlignItems::{align_items:?}")),
1056 TextFont::from_font_size(10.),
1057 ));
1058 if boxed {
1059 parent.spawn((
1060 Node {
1061 align_items,
1062 border: px(2).all(),
1063 padding: px(2).all(),
1064 flex_direction,
1065 ..default()
1066 },
1067 BorderColor::all(Color::WHITE),
1068 children![
1069 (
1070 Node {
1071 width: px(32),
1072 height: px(32),
1073 ..default()
1074 },
1075 BackgroundColor(Color::WHITE),
1076 ),
1077 (
1078 Node { width, ..default() },
1079 children![(
1080 Text::new("+300 Some Long Item Title"),
1081 TextFont {
1082 font_size: FontSize::Px(10.),
1083 ..default()
1084 },
1085 BackgroundColor(Color::srgba(
1086 0.95, 0.85, 0.2, 0.35
1087 )),
1088 )]
1089 )
1090 ],
1091 ));
1092 } else {
1093 parent.spawn((
1094 Node {
1095 align_items,
1096 border: px(2).all(),
1097 padding: px(2).all(),
1098 flex_direction,
1099 ..default()
1100 },
1101 BorderColor::all(Color::WHITE),
1102 children![
1103 (
1104 Node {
1105 width: px(32),
1106 height: px(32),
1107 ..default()
1108 },
1109 BackgroundColor(Color::WHITE),
1110 ),
1111 (
1112 Node { width, ..default() },
1113 Text::new("+300 Some Long Item Title"),
1114 TextFont {
1115 font_size: FontSize::Px(10.),
1116 ..default()
1117 },
1118 BackgroundColor(Color::srgba(
1119 0.95, 0.85, 0.2, 0.35
1120 )),
1121 )
1122 ],
1123 ));
1124 }
1125 }
1126 });
1127 }
1128 });
1129 }
1130}
1131
1132mod grid {
1133 use bevy::{color::palettes::css::*, prelude::*};
1134
1135 pub fn setup(mut commands: Commands) {
1136 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Grid)));
1137 // Top-level grid (app frame)
1138 commands.spawn((
1139 Node {
1140 display: Display::Grid,
1141 width: percent(100),
1142 height: percent(100),
1143 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
1144 grid_template_rows: vec![
1145 GridTrack::auto(),
1146 GridTrack::flex(1.0),
1147 GridTrack::px(40.),
1148 ],
1149 ..default()
1150 },
1151 BackgroundColor(Color::WHITE),
1152 DespawnOnExit(super::Scene::Grid),
1153 children![
1154 // Header
1155 (
1156 Node {
1157 display: Display::Grid,
1158 grid_column: GridPlacement::span(2),
1159 padding: UiRect::all(px(40)),
1160 ..default()
1161 },
1162 BackgroundColor(RED.into()),
1163 ),
1164 // Main content grid (auto placed in row 2, column 1)
1165 (
1166 Node {
1167 height: percent(100),
1168 aspect_ratio: Some(1.0),
1169 display: Display::Grid,
1170 grid_template_columns: RepeatedGridTrack::flex(3, 1.0),
1171 grid_template_rows: RepeatedGridTrack::flex(2, 1.0),
1172 row_gap: px(12),
1173 column_gap: px(12),
1174 ..default()
1175 },
1176 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
1177 children![
1178 (Node::default(), BackgroundColor(ORANGE.into())),
1179 (Node::default(), BackgroundColor(BISQUE.into())),
1180 (Node::default(), BackgroundColor(BLUE.into())),
1181 (Node::default(), BackgroundColor(CRIMSON.into())),
1182 (Node::default(), BackgroundColor(AQUA.into())),
1183 ]
1184 ),
1185 // Right side bar (auto placed in row 2, column 2)
1186 (Node::DEFAULT, BackgroundColor(BLACK.into())),
1187 ],
1188 ));
1189 }Sourcepub fn auto<T>() -> T
pub fn auto<T>() -> T
Create a grid track which is automatically sized to fit its contents.
Examples found in repository?
1135 pub fn setup(mut commands: Commands) {
1136 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Grid)));
1137 // Top-level grid (app frame)
1138 commands.spawn((
1139 Node {
1140 display: Display::Grid,
1141 width: percent(100),
1142 height: percent(100),
1143 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
1144 grid_template_rows: vec![
1145 GridTrack::auto(),
1146 GridTrack::flex(1.0),
1147 GridTrack::px(40.),
1148 ],
1149 ..default()
1150 },
1151 BackgroundColor(Color::WHITE),
1152 DespawnOnExit(super::Scene::Grid),
1153 children![
1154 // Header
1155 (
1156 Node {
1157 display: Display::Grid,
1158 grid_column: GridPlacement::span(2),
1159 padding: UiRect::all(px(40)),
1160 ..default()
1161 },
1162 BackgroundColor(RED.into()),
1163 ),
1164 // Main content grid (auto placed in row 2, column 1)
1165 (
1166 Node {
1167 height: percent(100),
1168 aspect_ratio: Some(1.0),
1169 display: Display::Grid,
1170 grid_template_columns: RepeatedGridTrack::flex(3, 1.0),
1171 grid_template_rows: RepeatedGridTrack::flex(2, 1.0),
1172 row_gap: px(12),
1173 column_gap: px(12),
1174 ..default()
1175 },
1176 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
1177 children![
1178 (Node::default(), BackgroundColor(ORANGE.into())),
1179 (Node::default(), BackgroundColor(BISQUE.into())),
1180 (Node::default(), BackgroundColor(BLUE.into())),
1181 (Node::default(), BackgroundColor(CRIMSON.into())),
1182 (Node::default(), BackgroundColor(AQUA.into())),
1183 ]
1184 ),
1185 // Right side bar (auto placed in row 2, column 2)
1186 (Node::DEFAULT, BackgroundColor(BLACK.into())),
1187 ],
1188 ));
1189 }
1190}
1191
1192mod borders {
1193 use bevy::{color::palettes::css::*, prelude::*};
1194
1195 pub fn setup(mut commands: Commands) {
1196 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Borders)));
1197 let root = commands
1198 .spawn((
1199 Node {
1200 flex_wrap: FlexWrap::Wrap,
1201 ..default()
1202 },
1203 DespawnOnExit(super::Scene::Borders),
1204 ))
1205 .id();
1206
1207 // all the different combinations of border edges
1208 let borders = [
1209 UiRect::default(),
1210 UiRect::all(px(20)),
1211 UiRect::left(px(20)),
1212 UiRect::vertical(px(20)),
1213 UiRect {
1214 left: px(40),
1215 top: px(20),
1216 ..Default::default()
1217 },
1218 UiRect {
1219 right: px(20),
1220 bottom: px(30),
1221 ..Default::default()
1222 },
1223 UiRect {
1224 right: px(20),
1225 top: px(40),
1226 bottom: px(20),
1227 ..Default::default()
1228 },
1229 UiRect {
1230 left: px(20),
1231 top: px(20),
1232 bottom: px(20),
1233 ..Default::default()
1234 },
1235 UiRect {
1236 left: px(20),
1237 right: px(20),
1238 bottom: px(40),
1239 ..Default::default()
1240 },
1241 ];
1242
1243 let non_zero = |x, y| x != px(0) && y != px(0);
1244 let border_size = |x, y| if non_zero(x, y) { f32::MAX } else { 0. };
1245
1246 for border in borders {
1247 for rounded in [true, false] {
1248 let border_node = commands
1249 .spawn((
1250 Node {
1251 width: px(100),
1252 height: px(100),
1253 border,
1254 margin: UiRect::all(px(30)),
1255 align_items: AlignItems::Center,
1256 justify_content: JustifyContent::Center,
1257 border_radius: if rounded {
1258 BorderRadius::px(
1259 border_size(border.left, border.top),
1260 border_size(border.right, border.top),
1261 border_size(border.right, border.bottom),
1262 border_size(border.left, border.bottom),
1263 )
1264 } else {
1265 BorderRadius::ZERO
1266 },
1267 ..default()
1268 },
1269 BackgroundColor(MAROON.into()),
1270 BorderColor::all(RED),
1271 Outline {
1272 width: px(10),
1273 offset: px(10),
1274 color: Color::WHITE,
1275 },
1276 ))
1277 .id();
1278
1279 commands.entity(root).add_child(border_node);
1280 }
1281 }
1282 }
1283}
1284
1285mod elliptical_border_radius {
1286 use bevy::{color::palettes::css::*, prelude::*};
1287
1288 pub fn setup(mut commands: Commands, assets: Res<AssetServer>) {
1289 commands.spawn((
1290 Camera2d,
1291 DespawnOnExit(super::Scene::EllipticalBorderRadius),
1292 ));
1293 commands
1294 .spawn((
1295 Node {
1296 width: percent(100),
1297 height: percent(100),
1298 flex_wrap: FlexWrap::Wrap,
1299 column_gap: px(40),
1300 row_gap: px(40),
1301 margin: auto().all(),
1302 padding: px(30).all(),
1303 ..default()
1304 },
1305 BackgroundColor(DARK_GRAY.into()),
1306 DespawnOnExit(super::Scene::EllipticalBorderRadius),
1307 ))
1308 .with_children(|builder| {
1309 builder.spawn((
1310 Node {
1311 width: px(200),
1312 height: px(100),
1313 border: UiRect::all(px(8)),
1314 border_radius: BorderRadius::new(
1315 CornerRadius {
1316 x: px(90),
1317 y: px(24),
1318 },
1319 CornerRadius {
1320 x: px(18),
1321 y: px(70),
1322 },
1323 CornerRadius {
1324 x: px(110),
1325 y: px(32),
1326 },
1327 CornerRadius {
1328 x: px(28),
1329 y: px(58),
1330 },
1331 ),
1332 ..default()
1333 },
1334 BackgroundColor(ORANGE.into()),
1335 BackgroundGradient::from(LinearGradient {
1336 stops: vec![
1337 RED.into(),
1338 Color::BLACK.into(),
1339 BLUE.into(),
1340 WHEAT.into(),
1341 GREEN.into(),
1342 ],
1343 ..default()
1344 }),
1345 BorderColor::all(RED),
1346 Outline {
1347 width: px(4),
1348 offset: px(8),
1349 color: WHITE.into(),
1350 },
1351 BoxShadow::from(ShadowStyle {
1352 blur_radius: px(5),
1353 ..default()
1354 }),
1355 ));
1356
1357 builder.spawn((
1358 Node {
1359 width: px(150),
1360 height: px(150),
1361 border: UiRect {
1362 left: px(16),
1363 right: px(4),
1364 top: px(24),
1365 bottom: px(8),
1366 },
1367 border_radius: BorderRadius::new(
1368 CornerRadius {
1369 x: percent(65),
1370 y: percent(20),
1371 },
1372 CornerRadius {
1373 x: percent(20),
1374 y: percent(65),
1375 },
1376 CornerRadius {
1377 x: percent(65),
1378 y: percent(20),
1379 },
1380 CornerRadius {
1381 x: percent(20),
1382 y: percent(65),
1383 },
1384 ),
1385 ..default()
1386 },
1387 BackgroundColor(MEDIUM_SEA_GREEN.into()),
1388 BorderColor::all(DARK_GREEN),
1389 Outline {
1390 width: px(3),
1391 offset: px(10),
1392 color: LIME.into(),
1393 },
1394 BoxShadow::from(ShadowStyle {
1395 blur_radius: px(5),
1396 ..default()
1397 }),
1398 ));
1399
1400 builder.spawn((
1401 Node {
1402 width: px(210),
1403 height: px(75),
1404 border: UiRect::axes(px(12), px(4)),
1405 border_radius: BorderRadius::new(
1406 CornerRadius {
1407 x: px(140),
1408 y: px(18),
1409 },
1410 CornerRadius {
1411 x: px(140),
1412 y: px(18),
1413 },
1414 CornerRadius {
1415 x: px(42),
1416 y: px(54),
1417 },
1418 CornerRadius {
1419 x: px(42),
1420 y: px(54),
1421 },
1422 ),
1423 ..default()
1424 },
1425 BackgroundColor(DODGER_BLUE.into()),
1426 BorderColor::all(NAVY),
1427 Outline {
1428 width: px(5),
1429 offset: px(6),
1430 color: SKY_BLUE.into(),
1431 },
1432 BoxShadow::from(ShadowStyle {
1433 blur_radius: px(5),
1434 ..default()
1435 }),
1436 ));
1437 builder.spawn((
1438 Node {
1439 width: px(160),
1440 height: px(120),
1441 border: UiRect::axes(px(20), px(20)),
1442 border_radius: BorderRadius::new(
1443 CornerRadius {
1444 x: px(50),
1445 y: px(10),
1446 },
1447 CornerRadius {
1448 x: px(50),
1449 y: px(10),
1450 },
1451 CornerRadius {
1452 x: px(50),
1453 y: px(10),
1454 },
1455 CornerRadius {
1456 x: px(50),
1457 y: px(10),
1458 },
1459 ),
1460 ..default()
1461 },
1462 BackgroundColor(RED.into()),
1463 BorderColor::all(WHITE),
1464 Outline {
1465 width: px(3),
1466 offset: px(5),
1467 color: SKY_BLUE.into(),
1468 },
1469 BoxShadow::from(ShadowStyle {
1470 blur_radius: px(5),
1471 ..default()
1472 }),
1473 ));
1474
1475 builder.spawn((
1476 Node {
1477 width: px(160),
1478 height: px(120),
1479 border: UiRect::axes(px(20), px(20)),
1480 border_radius: BorderRadius::all(px(1000)),
1481 ..default()
1482 },
1483 BackgroundColor(RED.into()),
1484 BorderColor::all(WHITE),
1485 Outline {
1486 width: px(3),
1487 offset: px(5),
1488 color: SKY_BLUE.into(),
1489 },
1490 BoxShadow::from(ShadowStyle {
1491 blur_radius: px(5),
1492 ..default()
1493 }),
1494 ));
1495
1496 builder.spawn((
1497 Node {
1498 width: px(160),
1499 height: px(120),
1500 border: UiRect::axes(px(20), px(20)),
1501 border_radius: BorderRadius {
1502 top_left: CornerRadius {
1503 x: px(1000),
1504 y: auto(),
1505 },
1506 top_right: CornerRadius {
1507 x: auto(),
1508 y: px(1000),
1509 },
1510 bottom_right: CornerRadius::circular(px(1000)),
1511 bottom_left: CornerRadius::circular(px(1000)),
1512 },
1513 ..default()
1514 },
1515 BackgroundColor(RED.into()),
1516 BorderColor::all(WHITE),
1517 Outline {
1518 width: px(3),
1519 offset: px(5),
1520 color: SKY_BLUE.into(),
1521 },
1522 BoxShadow::from(ShadowStyle {
1523 blur_radius: px(5),
1524 ..default()
1525 }),
1526 ));
1527
1528 builder.spawn((
1529 Node {
1530 width: px(160),
1531 height: px(120),
1532 border: UiRect::axes(px(20), px(20)),
1533 border_radius: BorderRadius::new(
1534 CornerRadius {
1535 x: px(25),
1536 y: px(20),
1537 },
1538 CornerRadius {
1539 x: px(20),
1540 y: px(25),
1541 },
1542 CornerRadius {
1543 x: px(20),
1544 y: px(25),
1545 },
1546 CornerRadius {
1547 x: px(20),
1548 y: px(25),
1549 },
1550 ),
1551 ..default()
1552 },
1553 BackgroundColor(RED.into()),
1554 ImageNode::from(assets.load("branding/icon.png")),
1555 BorderColor::all(WHITE),
1556 Outline {
1557 width: px(3),
1558 offset: px(5),
1559 color: SKY_BLUE.into(),
1560 },
1561 BoxShadow::from(ShadowStyle {
1562 blur_radius: px(5),
1563 ..default()
1564 }),
1565 ));
1566
1567 builder.spawn((
1568 Node {
1569 width: px(160),
1570 height: px(120),
1571 border: UiRect::axes(px(10), px(10)),
1572 border_radius: BorderRadius::new(
1573 CornerRadius {
1574 x: px(40),
1575 y: px(30),
1576 },
1577 CornerRadius {
1578 x: px(40),
1579 y: px(30),
1580 },
1581 CornerRadius {
1582 x: px(40),
1583 y: px(30),
1584 },
1585 CornerRadius {
1586 x: px(40),
1587 y: px(30),
1588 },
1589 ),
1590 ..default()
1591 },
1592 BackgroundColor(RED.into()),
1593 BorderColor::all(WHITE),
1594 Outline {
1595 width: px(3),
1596 offset: px(5),
1597 color: SKY_BLUE.into(),
1598 },
1599 BoxShadow::from(ShadowStyle {
1600 blur_radius: px(5),
1601 ..default()
1602 }),
1603 ));
1604
1605 builder.spawn((
1606 Node {
1607 width: px(160),
1608 height: px(80),
1609 border: UiRect::axes(px(10), px(10)),
1610 border_radius: BorderRadius::new(
1611 CornerRadius {
1612 x: percent(50),
1613 y: percent(50),
1614 },
1615 CornerRadius {
1616 x: percent(50),
1617 y: percent(50),
1618 },
1619 CornerRadius {
1620 x: percent(50),
1621 y: percent(50),
1622 },
1623 CornerRadius {
1624 x: percent(50),
1625 y: percent(50),
1626 },
1627 ),
1628 ..default()
1629 },
1630 BackgroundColor(RED.into()),
1631 BorderColor::all(WHITE),
1632 ImageNode::from(assets.load("branding/icon.png")),
1633 Outline {
1634 width: px(3),
1635 offset: px(5),
1636 color: SKY_BLUE.into(),
1637 },
1638 BoxShadow::from(ShadowStyle {
1639 blur_radius: px(5),
1640 ..default()
1641 }),
1642 ));
1643
1644 builder.spawn((
1645 Node {
1646 width: px(80),
1647 height: px(160),
1648 border: UiRect::axes(px(10), px(10)),
1649 border_radius: BorderRadius::new(
1650 CornerRadius {
1651 x: percent(50),
1652 y: percent(50),
1653 },
1654 CornerRadius {
1655 x: percent(50),
1656 y: percent(50),
1657 },
1658 CornerRadius {
1659 x: percent(50),
1660 y: percent(50),
1661 },
1662 CornerRadius {
1663 x: percent(50),
1664 y: percent(50),
1665 },
1666 ),
1667 ..default()
1668 },
1669 BackgroundColor(RED.into()),
1670 BorderColor::all(WHITE),
1671 ImageNode::from(assets.load("branding/icon.png")),
1672 Outline {
1673 width: px(3),
1674 offset: px(5),
1675 color: SKY_BLUE.into(),
1676 },
1677 BoxShadow::from(ShadowStyle {
1678 blur_radius: px(5),
1679 ..default()
1680 }),
1681 ));
1682
1683 builder.spawn((
1684 Node {
1685 width: px(160),
1686 height: px(80),
1687 border: UiRect::axes(px(20), px(10)),
1688 border_radius: BorderRadius::new(
1689 CornerRadius {
1690 x: percent(50),
1691 y: percent(50),
1692 },
1693 CornerRadius {
1694 x: percent(50),
1695 y: percent(50),
1696 },
1697 CornerRadius {
1698 x: percent(50),
1699 y: percent(50),
1700 },
1701 CornerRadius {
1702 x: percent(50),
1703 y: percent(50),
1704 },
1705 ),
1706 ..default()
1707 },
1708 BackgroundColor(RED.into()),
1709 BorderColor::all(WHITE),
1710 ImageNode::from(assets.load("branding/icon.png")),
1711 Outline {
1712 width: px(3),
1713 offset: px(5),
1714 color: SKY_BLUE.into(),
1715 },
1716 BoxShadow::from(ShadowStyle {
1717 blur_radius: px(5),
1718 ..default()
1719 }),
1720 ));
1721
1722 builder.spawn((
1723 Node {
1724 width: px(80),
1725 height: px(160),
1726 border: UiRect::all(px(10)).with_right(px(25)),
1727 border_radius: BorderRadius::new(
1728 CornerRadius {
1729 x: percent(50),
1730 y: percent(50),
1731 },
1732 CornerRadius {
1733 x: percent(50),
1734 y: percent(50),
1735 },
1736 CornerRadius {
1737 x: percent(50),
1738 y: percent(50),
1739 },
1740 CornerRadius {
1741 x: percent(50),
1742 y: percent(50),
1743 },
1744 ),
1745 ..default()
1746 },
1747 BackgroundColor(RED.into()),
1748 BorderColor::all(WHITE),
1749 Outline {
1750 width: px(3),
1751 offset: px(5),
1752 color: SKY_BLUE.into(),
1753 },
1754 BoxShadow::from(ShadowStyle {
1755 blur_radius: px(5),
1756 ..default()
1757 }),
1758 ));
1759
1760 builder.spawn((
1761 Node {
1762 width: px(160),
1763 height: px(80),
1764 border: UiRect::all(px(5)),
1765 border_radius: BorderRadius::new(
1766 CornerRadius {
1767 x: percent(50),
1768 y: percent(50),
1769 },
1770 CornerRadius {
1771 x: percent(50),
1772 y: percent(50),
1773 },
1774 CornerRadius {
1775 x: percent(50),
1776 y: percent(50),
1777 },
1778 CornerRadius {
1779 x: px(20),
1780 y: px(20),
1781 },
1782 ),
1783 ..default()
1784 },
1785 BackgroundColor(RED.into()),
1786 BorderColor::all(WHITE),
1787 ImageNode::from(assets.load("branding/icon.png")),
1788 Outline {
1789 width: px(3),
1790 offset: px(5),
1791 color: SKY_BLUE.into(),
1792 },
1793 BoxShadow::from(ShadowStyle {
1794 blur_radius: px(5),
1795 ..default()
1796 }),
1797 ));
1798 });
1799 }
1800}
1801
1802mod box_shadow {
1803 use bevy::{color::palettes::css::*, prelude::*};
1804
1805 pub fn setup(mut commands: Commands) {
1806 commands.spawn((Camera2d, DespawnOnExit(super::Scene::BoxShadow)));
1807
1808 commands
1809 .spawn((
1810 Node {
1811 width: percent(100),
1812 height: percent(100),
1813 padding: UiRect::all(px(30)),
1814 column_gap: px(200),
1815 flex_wrap: FlexWrap::Wrap,
1816 ..default()
1817 },
1818 BackgroundColor(GREEN.into()),
1819 DespawnOnExit(super::Scene::BoxShadow),
1820 ))
1821 .with_children(|commands| {
1822 let example_nodes = [
1823 (
1824 Vec2::splat(100.),
1825 Vec2::ZERO,
1826 10.,
1827 0.,
1828 BorderRadius::bottom_right(px(10)),
1829 ),
1830 (Vec2::new(200., 50.), Vec2::ZERO, 10., 0., BorderRadius::MAX),
1831 (
1832 Vec2::new(100., 50.),
1833 Vec2::ZERO,
1834 10.,
1835 10.,
1836 BorderRadius::ZERO,
1837 ),
1838 (
1839 Vec2::splat(100.),
1840 Vec2::splat(20.),
1841 10.,
1842 10.,
1843 BorderRadius::bottom_right(px(10)),
1844 ),
1845 (
1846 Vec2::splat(100.),
1847 Vec2::splat(50.),
1848 0.,
1849 10.,
1850 BorderRadius::ZERO,
1851 ),
1852 (
1853 Vec2::new(50., 100.),
1854 Vec2::splat(10.),
1855 0.,
1856 10.,
1857 BorderRadius::MAX,
1858 ),
1859 ];
1860
1861 for (size, offset, spread, blur, border_radius) in example_nodes {
1862 commands.spawn((
1863 Node {
1864 width: px(size.x),
1865 height: px(size.y),
1866 border: UiRect::all(px(2)),
1867 border_radius,
1868 ..default()
1869 },
1870 BorderColor::all(WHITE),
1871 BackgroundColor(BLUE.into()),
1872 BoxShadow::new(
1873 Color::BLACK.with_alpha(0.9),
1874 percent(offset.x),
1875 percent(offset.y),
1876 percent(spread),
1877 px(blur),
1878 ),
1879 ));
1880 }
1881 });
1882 }
1883}
1884
1885mod text_wrap {
1886 use bevy::prelude::*;
1887
1888 pub fn setup(mut commands: Commands) {
1889 commands.spawn((Camera2d, DespawnOnExit(super::Scene::TextWrap)));
1890
1891 let root = commands
1892 .spawn((
1893 Node {
1894 flex_direction: FlexDirection::Column,
1895 width: px(200),
1896 height: percent(100),
1897 overflow: Overflow::clip_x(),
1898 ..default()
1899 },
1900 BackgroundColor(Color::BLACK),
1901 DespawnOnExit(super::Scene::TextWrap),
1902 ))
1903 .id();
1904
1905 for linebreak in [
1906 LineBreak::AnyCharacter,
1907 LineBreak::WordBoundary,
1908 LineBreak::WordOrCharacter,
1909 LineBreak::NoWrap,
1910 ] {
1911 let messages = [
1912 "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".to_string(),
1913 "pneumonoultramicroscopicsilicovolcanoconiosis".to_string(),
1914 ];
1915
1916 for (j, message) in messages.into_iter().enumerate() {
1917 commands.entity(root).with_child((
1918 Text(message.clone()),
1919 TextLayout::new(Justify::Left, linebreak),
1920 BackgroundColor(Color::srgb(0.8 - j as f32 * 0.3, 0., 0.)),
1921 ));
1922 }
1923 }
1924 }
1925}
1926
1927mod overflow {
1928 use bevy::{color::palettes::css::*, prelude::*};
1929
1930 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
1931 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Overflow)));
1932 let image = asset_server.load("branding/icon.png");
1933
1934 commands
1935 .spawn((
1936 Node {
1937 width: percent(100),
1938 height: percent(100),
1939 align_items: AlignItems::Center,
1940 justify_content: JustifyContent::SpaceAround,
1941 ..Default::default()
1942 },
1943 BackgroundColor(BLUE.into()),
1944 DespawnOnExit(super::Scene::Overflow),
1945 ))
1946 .with_children(|parent| {
1947 for overflow in [
1948 Overflow::visible(),
1949 Overflow::clip_x(),
1950 Overflow::clip_y(),
1951 Overflow::clip(),
1952 ] {
1953 parent
1954 .spawn((
1955 Node {
1956 width: px(100),
1957 height: px(100),
1958 padding: UiRect {
1959 left: px(25),
1960 top: px(25),
1961 ..Default::default()
1962 },
1963 border: UiRect::all(px(5)),
1964 overflow,
1965 ..default()
1966 },
1967 BorderColor::all(RED),
1968 BackgroundColor(Color::WHITE),
1969 ))
1970 .with_children(|parent| {
1971 parent.spawn((
1972 ImageNode::new(image.clone()),
1973 Node {
1974 min_width: px(100),
1975 min_height: px(100),
1976 ..default()
1977 },
1978 Outline {
1979 width: px(2),
1980 offset: px(2),
1981 color: Color::NONE,
1982 },
1983 ));
1984 });
1985 }
1986 });
1987 }
1988}
1989
1990mod slice {
1991 use bevy::prelude::*;
1992
1993 pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
1994 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Slice)));
1995 let image = asset_server.load("textures/fantasy_ui_borders/numbered_slices.png");
1996
1997 let slicer = TextureSlicer {
1998 border: BorderRect::all(16.0),
1999 center_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
2000 sides_scale_mode: SliceScaleMode::Tile { stretch_value: 1.0 },
2001 ..default()
2002 };
2003 commands
2004 .spawn((
2005 Node {
2006 width: percent(100),
2007 height: percent(100),
2008 flex_direction: FlexDirection::Column,
2009 justify_content: JustifyContent::SpaceAround,
2010 align_content: AlignContent::Center,
2011 ..default()
2012 },
2013 DespawnOnExit(super::Scene::Slice),
2014 ))
2015 .with_children(|parent| {
2016 for visual_box in [
2017 VisualBox::BorderBox,
2018 VisualBox::PaddingBox,
2019 VisualBox::ContentBox,
2020 ] {
2021 parent
2022 .spawn(Node {
2023 justify_content: JustifyContent::SpaceAround,
2024 ..default()
2025 })
2026 .with_children(|parent| {
2027 for [w, h] in [[200.0, 200.0], [300.0, 200.0], [150., 200.0]] {
2028 parent.spawn((
2029 ImageNode {
2030 image: image.clone(),
2031 image_mode: NodeImageMode::Sliced(slicer.clone()),
2032 visual_box,
2033 ..default()
2034 },
2035 Node {
2036 width: px(w),
2037 height: px(h),
2038 border: px(20.).all(),
2039 padding: px(20.).all(),
2040 ..default()
2041 },
2042 Outline {
2043 width: px(2.),
2044 ..default()
2045 },
2046 ));
2047 }
2048
2049 parent.spawn((
2050 ImageNode {
2051 image: asset_server
2052 .load("textures/fantasy_ui_borders/panel-border-010.png"),
2053 image_mode: NodeImageMode::Sliced(TextureSlicer {
2054 border: BorderRect::all(22.0),
2055 center_scale_mode: SliceScaleMode::Stretch,
2056 sides_scale_mode: SliceScaleMode::Stretch,
2057 max_corner_scale: 1.0,
2058 }),
2059 visual_box,
2060 ..Default::default()
2061 },
2062 Node {
2063 width: px(200),
2064 height: px(200),
2065 border: px(20.).all(),
2066 padding: px(20.).all(),
2067 ..default()
2068 },
2069 Outline {
2070 color: bevy::color::palettes::css::DARK_CYAN.into(),
2071 width: px(2.),
2072 ..default()
2073 },
2074 BackgroundColor(bevy::color::palettes::css::NAVY.into()),
2075 ));
2076 });
2077 }
2078 });
2079 }
2080}
2081
2082mod layout_rounding {
2083 use bevy::{color::palettes::css::*, prelude::*};
2084
2085 pub fn setup(mut commands: Commands) {
2086 commands.spawn((Camera2d, DespawnOnExit(super::Scene::LayoutRounding)));
2087
2088 commands
2089 .spawn((
2090 Node {
2091 display: Display::Grid,
2092 width: percent(100),
2093 height: percent(100),
2094 grid_template_rows: vec![RepeatedGridTrack::fr(10, 1.)],
2095 ..Default::default()
2096 },
2097 BackgroundColor(Color::WHITE),
2098 DespawnOnExit(super::Scene::LayoutRounding),
2099 ))
2100 .with_children(|commands| {
2101 for i in 2..12 {
2102 commands
2103 .spawn(Node {
2104 display: Display::Grid,
2105 grid_template_columns: vec![RepeatedGridTrack::fr(i, 1.)],
2106 ..Default::default()
2107 })
2108 .with_children(|commands| {
2109 for _ in 0..i {
2110 commands.spawn((
2111 Node {
2112 border: UiRect::all(px(5)),
2113 ..Default::default()
2114 },
2115 BackgroundColor(MAROON.into()),
2116 BorderColor::all(DARK_BLUE),
2117 ));
2118 }
2119 });
2120 }
2121 });
2122 }
2123}
2124
2125mod linear_gradient {
2126 use bevy::camera::Camera2d;
2127 use bevy::color::palettes::css::BLUE;
2128 use bevy::color::palettes::css::LIME;
2129 use bevy::color::palettes::css::RED;
2130 use bevy::color::palettes::css::YELLOW;
2131 use bevy::color::Color;
2132 use bevy::ecs::prelude::*;
2133 use bevy::state::state_scoped::DespawnOnExit;
2134 use bevy::text::TextFont;
2135 use bevy::ui::widget::Text;
2136 use bevy::ui::AlignItems;
2137 use bevy::ui::BackgroundGradient;
2138 use bevy::ui::ColorStop;
2139 use bevy::ui::GridPlacement;
2140 use bevy::ui::InterpolationColorSpace;
2141 use bevy::ui::JustifyContent;
2142 use bevy::ui::LinearGradient;
2143 use bevy::ui::Node;
2144 use bevy::ui::PositionType;
2145 use bevy::utils::default;
2146
2147 pub fn setup(mut commands: Commands) {
2148 commands.spawn((Camera2d, DespawnOnExit(super::Scene::LinearGradient)));
2149 commands
2150 .spawn((
2151 Node {
2152 flex_direction: bevy::ui::FlexDirection::Column,
2153 width: bevy::ui::percent(100),
2154 height: bevy::ui::percent(100),
2155 justify_content: JustifyContent::Center,
2156 align_items: AlignItems::Center,
2157 row_gap: bevy::ui::px(5),
2158 ..default()
2159 },
2160 DespawnOnExit(super::Scene::LinearGradient),
2161 ))
2162 .with_children(|commands| {
2163 let mut i = 0;
2164 commands
2165 .spawn(Node {
2166 display: bevy::ui::Display::Grid,
2167 row_gap: bevy::ui::px(4),
2168 column_gap: bevy::ui::px(4),
2169 ..Default::default()
2170 })
2171 .with_children(|commands| {
2172 for stops in [
2173 vec![ColorStop::auto(RED), ColorStop::auto(YELLOW)],
2174 vec![
2175 ColorStop::auto(Color::BLACK),
2176 ColorStop::auto(RED),
2177 ColorStop::auto(Color::WHITE),
2178 ],
2179 vec![
2180 Color::hsl(180.71191, 0.0, 0.3137255).into(),
2181 Color::hsl(180.71191, 0.5, 0.3137255).into(),
2182 Color::hsl(180.71191, 1.0, 0.3137255).into(),
2183 ],
2184 vec![
2185 Color::hsl(180.71191, 0.825, 0.0).into(),
2186 Color::hsl(180.71191, 0.825, 0.5).into(),
2187 Color::hsl(180.71191, 0.825, 1.0).into(),
2188 ],
2189 vec![
2190 Color::hsl(0.0 + 0.0001, 1.0, 0.5).into(),
2191 Color::hsl(180.0, 1.0, 0.5).into(),
2192 Color::hsl(360.0 - 0.0001, 1.0, 0.5).into(),
2193 ],
2194 vec![
2195 Color::WHITE.into(),
2196 RED.into(),
2197 LIME.into(),
2198 BLUE.into(),
2199 Color::BLACK.into(),
2200 ],
2201 ] {
2202 for color_space in [
2203 InterpolationColorSpace::LinearRgba,
2204 InterpolationColorSpace::Srgba,
2205 InterpolationColorSpace::Oklaba,
2206 InterpolationColorSpace::Oklcha,
2207 InterpolationColorSpace::OklchaLong,
2208 InterpolationColorSpace::Hsla,
2209 InterpolationColorSpace::HslaLong,
2210 InterpolationColorSpace::Hsva,
2211 InterpolationColorSpace::HsvaLong,
2212 ] {
2213 let row = i % 18 + 1;
2214 let column = i / 18 + 1;
2215 i += 1;
2216
2217 commands.spawn((
2218 Node {
2219 grid_row: GridPlacement::start(row as i16 + 1),
2220 grid_column: GridPlacement::start(column as i16 + 1),
2221 justify_content: JustifyContent::SpaceEvenly,
2222 ..Default::default()
2223 },
2224 children![(
2225 Node {
2226 height: bevy::ui::px(30),
2227 width: bevy::ui::px(300),
2228 justify_content: JustifyContent::Center,
2229 ..Default::default()
2230 },
2231 BackgroundGradient::from(LinearGradient {
2232 color_space,
2233 angle: LinearGradient::TO_RIGHT,
2234 stops: stops.clone(),
2235 }),
2236 children![
2237 Node {
2238 position_type: PositionType::Absolute,
2239 ..default()
2240 },
2241 TextFont::from_font_size(10.),
2242 Text(format!("{color_space:?}")),
2243 ]
2244 )],
2245 ));
2246 }
2247 }
2248 });
2249 });
2250 }
2251}
2252
2253mod radial_gradient {
2254 use bevy::color::palettes::css::RED;
2255 use bevy::color::palettes::tailwind::GRAY_700;
2256 use bevy::prelude::*;
2257 use bevy::ui::ColorStop;
2258
2259 const CELL_SIZE: f32 = 80.;
2260 const GAP: f32 = 10.;
2261
2262 pub fn setup(mut commands: Commands) {
2263 let color_stops = vec![
2264 ColorStop::new(Color::BLACK, px(5)),
2265 ColorStop::new(Color::WHITE, px(5)),
2266 ColorStop::new(Color::WHITE, percent(100)),
2267 ColorStop::auto(RED),
2268 ];
2269
2270 commands.spawn((Camera2d, DespawnOnExit(super::Scene::RadialGradient)));
2271 commands
2272 .spawn((
2273 Node {
2274 width: percent(100),
2275 height: percent(100),
2276 display: Display::Grid,
2277 align_items: AlignItems::Start,
2278 grid_template_columns: vec![RepeatedGridTrack::px(
2279 GridTrackRepetition::AutoFill,
2280 CELL_SIZE,
2281 )],
2282 grid_auto_flow: GridAutoFlow::Row,
2283 row_gap: px(GAP),
2284 column_gap: px(GAP),
2285 padding: UiRect::all(px(GAP)),
2286 ..default()
2287 },
2288 DespawnOnExit(super::Scene::RadialGradient),
2289 ))
2290 .with_children(|commands| {
2291 for (shape, shape_label) in [
2292 (RadialGradientShape::ClosestSide, "ClosestSide"),
2293 (RadialGradientShape::FarthestSide, "FarthestSide"),
2294 (RadialGradientShape::Circle(percent(55)), "Circle(55%)"),
2295 (RadialGradientShape::FarthestCorner, "FarthestCorner"),
2296 ] {
2297 for (position, position_label) in [
2298 (UiPosition::TOP_LEFT, "TOP_LEFT"),
2299 (UiPosition::LEFT, "LEFT"),
2300 (UiPosition::BOTTOM_LEFT, "BOTTOM_LEFT"),
2301 (UiPosition::TOP, "TOP"),
2302 (UiPosition::CENTER, "CENTER"),
2303 (UiPosition::BOTTOM, "BOTTOM"),
2304 (UiPosition::TOP_RIGHT, "TOP_RIGHT"),
2305 (UiPosition::RIGHT, "RIGHT"),
2306 (UiPosition::BOTTOM_RIGHT, "BOTTOM_RIGHT"),
2307 ] {
2308 for (w, h) in [(CELL_SIZE, CELL_SIZE), (CELL_SIZE, CELL_SIZE / 2.)] {
2309 commands
2310 .spawn((
2311 BackgroundColor(GRAY_700.into()),
2312 Node {
2313 display: Display::Grid,
2314 width: px(CELL_SIZE),
2315 ..Default::default()
2316 },
2317 ))
2318 .with_children(|commands| {
2319 commands.spawn((
2320 Node {
2321 margin: UiRect::all(px(2)),
2322 ..default()
2323 },
2324 Text(format!("{shape_label}\n{position_label}")),
2325 TextFont::from_font_size(9.),
2326 ));
2327 commands.spawn((
2328 Node {
2329 width: px(w),
2330 height: px(h),
2331 ..default()
2332 },
2333 BackgroundGradient::from(RadialGradient {
2334 stops: color_stops.clone(),
2335 position,
2336 shape,
2337 ..default()
2338 }),
2339 ));
2340 });
2341 }
2342 }
2343 }
2344 });
2345 }
2346}
2347
2348mod transformations {
2349 use bevy::{color::palettes::css::*, prelude::*};
2350
2351 pub fn setup(mut commands: Commands) {
2352 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Transformations)));
2353 commands
2354 .spawn((
2355 Node {
2356 width: percent(100),
2357 height: percent(100),
2358 display: Display::Block,
2359 ..default()
2360 },
2361 DespawnOnExit(super::Scene::Transformations),
2362 ))
2363 .with_children(|parent| {
2364 for (transformation, label, background) in [
2365 (
2366 UiTransform::from_rotation(Rot2::degrees(45.)),
2367 "Rotate 45 degrees",
2368 RED,
2369 ),
2370 (
2371 UiTransform::from_scale(Vec2::new(2., 0.5)),
2372 "Scale 2.x 0.5y",
2373 GREEN,
2374 ),
2375 (
2376 UiTransform::from_translation(Val2::px(-50., 50.)),
2377 "Translate -50px x +50px y",
2378 BLUE,
2379 ),
2380 (
2381 UiTransform {
2382 translation: Val2::px(50., 0.),
2383 scale: Vec2::new(-1., 1.),
2384 rotation: Rot2::degrees(30.),
2385 },
2386 "T 50px x\nS -1.x (refl)\nR 30deg",
2387 DARK_CYAN,
2388 ),
2389 ] {
2390 parent
2391 .spawn((Node {
2392 width: percent(100),
2393 margin: UiRect {
2394 top: px(50),
2395 bottom: px(50),
2396 ..default()
2397 },
2398 align_items: AlignItems::Center,
2399 justify_content: JustifyContent::SpaceAround,
2400 ..default()
2401 },))
2402 .with_children(|row| {
2403 row.spawn((
2404 Text::new("Before Tf"),
2405 Node {
2406 width: px(100),
2407 height: px(100),
2408 border_radius: BorderRadius::bottom_right(px(25.)),
2409 ..default()
2410 },
2411 BackgroundColor(background.into()),
2412 TextFont::default(),
2413 ));
2414 row.spawn((
2415 Text::new(label),
2416 Node {
2417 width: px(100),
2418 height: px(100),
2419 border_radius: BorderRadius::bottom_right(px(25.)),
2420 ..default()
2421 },
2422 BackgroundColor(background.into()),
2423 transformation,
2424 TextFont::default(),
2425 ));
2426 });
2427 }
2428 });
2429 }
2430}
2431
2432#[cfg(feature = "bevy_ui_debug")]
2433mod debug_outlines {
2434 use bevy::{
2435 color::palettes::css::{BLUE, GRAY, RED},
2436 prelude::*,
2437 ui_render::UiDebugOptions,
2438 };
2439
2440 pub fn setup(mut commands: Commands, mut debug_options: ResMut<GlobalUiDebugOptions>) {
2441 debug_options.enabled = true;
2442 debug_options.line_width = 5.;
2443 debug_options.line_color_override = Some(LinearRgba::GREEN);
2444 debug_options.show_hidden = true;
2445 debug_options.show_clipped = true;
2446
2447 let debug_options: UiDebugOptions = (*debug_options.as_ref()).into();
2448
2449 commands.spawn((Camera2d, DespawnOnExit(super::Scene::DebugOutlines)));
2450 commands
2451 .spawn((
2452 Node {
2453 width: percent(100),
2454 height: percent(50),
2455 align_items: AlignItems::Center,
2456 justify_content: JustifyContent::SpaceAround,
2457 ..default()
2458 },
2459 DespawnOnExit(super::Scene::DebugOutlines),
2460 ))
2461 .with_children(|parent| {
2462 parent.spawn((
2463 Node {
2464 width: px(100),
2465 height: px(100),
2466 ..default()
2467 },
2468 BackgroundColor(GRAY.into()),
2469 UiTransform::from_rotation(Rot2::degrees(45.)),
2470 ));
2471
2472 parent.spawn((Text::new("Regular Text"), TextFont::default()));
2473
2474 parent.spawn((
2475 Node {
2476 width: px(100),
2477 height: px(100),
2478 ..default()
2479 },
2480 Text::new("Invisible"),
2481 BackgroundColor(GRAY.into()),
2482 TextFont::default(),
2483 Visibility::Hidden,
2484 ));
2485
2486 parent
2487 .spawn((
2488 Node {
2489 width: px(100),
2490 height: px(100),
2491 padding: UiRect {
2492 left: px(25),
2493 top: px(25),
2494 ..Default::default()
2495 },
2496 overflow: Overflow::clip(),
2497 ..default()
2498 },
2499 BackgroundColor(RED.into()),
2500 ))
2501 .with_children(|child| {
2502 child.spawn((
2503 Node {
2504 min_width: px(100),
2505 min_height: px(100),
2506 ..default()
2507 },
2508 BackgroundColor(BLUE.into()),
2509 ));
2510 });
2511 });
2512
2513 commands
2514 .spawn((
2515 Node {
2516 width: percent(100),
2517 height: percent(50),
2518 top: percent(50),
2519 align_items: AlignItems::Center,
2520 justify_content: JustifyContent::SpaceAround,
2521 ..default()
2522 },
2523 DespawnOnExit(super::Scene::DebugOutlines),
2524 ))
2525 .with_children(|parent| {
2526 parent.spawn((
2527 Node {
2528 width: px(200),
2529 height: px(200),
2530 border: UiRect {
2531 top: px(10),
2532 bottom: px(20),
2533 left: px(30),
2534 right: px(40),
2535 },
2536 border_radius: BorderRadius::bottom_right(px(10)),
2537 padding: UiRect {
2538 top: px(40),
2539 bottom: px(30),
2540 left: px(20),
2541 right: px(10),
2542 },
2543 ..default()
2544 },
2545 children![(
2546 Text::new("border padding content outlines"),
2547 TextFont::default(),
2548 UiDebugOptions {
2549 enabled: false,
2550 ..default()
2551 }
2552 )],
2553 UiDebugOptions {
2554 outline_border_box: true,
2555 outline_padding_box: true,
2556 outline_content_box: true,
2557 ignore_border_radius: false,
2558 ..debug_options
2559 },
2560 ));
2561
2562 // Vertical scrollbar (non-functional)
2563 parent.spawn((
2564 Node {
2565 flex_direction: FlexDirection::Column,
2566 width: px(90),
2567 height: px(230),
2568 overflow: Overflow::scroll_y(),
2569 scrollbar_width: 20.,
2570 ..default()
2571 },
2572 ScrollPosition(Vec2::new(180., 180.)),
2573 UiDebugOptions {
2574 line_width: 3.,
2575 outline_scrollbars: true,
2576 show_hidden: false,
2577 show_clipped: false,
2578 ..debug_options
2579 },
2580 Children::spawn(SpawnIter((0..20).map(move |i| {
2581 (
2582 Node::default(),
2583 children![(
2584 Text(format!("Item {i}")),
2585 UiDebugOptions {
2586 enabled: false,
2587 ..default()
2588 }
2589 )],
2590 UiDebugOptions {
2591 enabled: false,
2592 ..default()
2593 },
2594 )
2595 }))),
2596 ));
2597
2598 // Horizontal scrollbar (non-functional)
2599 parent.spawn((
2600 Node {
2601 flex_direction: FlexDirection::Row,
2602 width: px(156),
2603 height: px(70),
2604 overflow: Overflow::scroll_x(),
2605 scrollbar_width: 10.,
2606 ..default()
2607 },
2608 UiDebugOptions {
2609 line_width: 3.,
2610 outline_scrollbars: true,
2611 show_hidden: false,
2612 show_clipped: false,
2613 ..debug_options
2614 },
2615 Children::spawn(SpawnIter((0..20).map(move |i| {
2616 (
2617 Node::default(),
2618 children![(
2619 Text(format!("Item {i}")),
2620 UiDebugOptions {
2621 enabled: false,
2622 ..default()
2623 }
2624 )],
2625 UiDebugOptions {
2626 enabled: false,
2627 ..default()
2628 },
2629 )
2630 }))),
2631 ));
2632
2633 // bi-directional scrollbar (non-functional)
2634 parent.spawn((
2635 Node {
2636 flex_direction: FlexDirection::Column,
2637 width: px(230),
2638 height: px(125),
2639 overflow: Overflow::scroll(),
2640 scrollbar_width: 20.,
2641 ..default()
2642 },
2643 ScrollPosition(Vec2::new(300., 0.)),
2644 UiDebugOptions {
2645 line_width: 3.,
2646 outline_scrollbars: true,
2647 show_hidden: false,
2648 show_clipped: false,
2649 ..debug_options
2650 },
2651 Children::spawn(SpawnIter((0..6).map(move |i| {
2652 (
2653 Node {
2654 flex_direction: FlexDirection::Row,
2655 ..default()
2656 },
2657 Children::spawn(SpawnIter((0..6).map({
2658 move |j| {
2659 (
2660 Text(format!("Item {}", (i * 5) + j)),
2661 UiDebugOptions {
2662 enabled: false,
2663 ..default()
2664 },
2665 )
2666 }
2667 }))),
2668 UiDebugOptions {
2669 enabled: false,
2670 ..default()
2671 },
2672 )
2673 }))),
2674 ));
2675 });
2676 }
2677
2678 pub fn teardown(mut debug_options: ResMut<GlobalUiDebugOptions>) {
2679 *debug_options = GlobalUiDebugOptions::default();
2680 }
2681}
2682
2683mod viewport_coords {
2684 use bevy::{color::palettes::css::*, prelude::*};
2685
2686 const PALETTE: [Srgba; 9] = [RED, WHITE, BEIGE, AQUA, CRIMSON, NAVY, AZURE, LIME, BLACK];
2687
2688 pub fn setup(mut commands: Commands) {
2689 commands.spawn((Camera2d, DespawnOnExit(super::Scene::ViewportCoords)));
2690 commands
2691 .spawn((
2692 Node {
2693 width: vw(100),
2694 height: vh(100),
2695 border: UiRect::axes(vw(5), vh(5)),
2696 flex_wrap: FlexWrap::Wrap,
2697 ..default()
2698 },
2699 BorderColor::all(PALETTE[0]),
2700 DespawnOnExit(super::Scene::ViewportCoords),
2701 ))
2702 .with_children(|builder| {
2703 builder.spawn((
2704 Node {
2705 width: vw(30),
2706 height: vh(30),
2707 border: UiRect::all(vmin(5)),
2708 ..default()
2709 },
2710 BackgroundColor(PALETTE[1].into()),
2711 BorderColor::all(PALETTE[8]),
2712 ));
2713
2714 builder.spawn((
2715 Node {
2716 width: vw(60),
2717 height: vh(30),
2718 ..default()
2719 },
2720 BackgroundColor(PALETTE[2].into()),
2721 ));
2722
2723 builder.spawn((
2724 Node {
2725 width: vw(45),
2726 height: vh(30),
2727 border: UiRect::left(vmax(45. / 2.)),
2728 ..default()
2729 },
2730 BackgroundColor(PALETTE[3].into()),
2731 BorderColor::all(PALETTE[7]),
2732 ));
2733
2734 builder.spawn((
2735 Node {
2736 width: vw(45),
2737 height: vh(30),
2738 border: UiRect::right(vmax(45. / 2.)),
2739 ..default()
2740 },
2741 BackgroundColor(PALETTE[4].into()),
2742 BorderColor::all(PALETTE[7]),
2743 ));
2744
2745 builder.spawn((
2746 Node {
2747 width: vw(60),
2748 height: vh(30),
2749 ..default()
2750 },
2751 BackgroundColor(PALETTE[5].into()),
2752 ));
2753
2754 builder.spawn((
2755 Node {
2756 width: vw(30),
2757 height: vh(30),
2758 border: UiRect::all(vmin(5)),
2759 ..default()
2760 },
2761 BackgroundColor(PALETTE[6].into()),
2762 BorderColor::all(PALETTE[8]),
2763 ));
2764 });
2765 }
2766}
2767
2768mod viewport_node {
2769 use bevy::{
2770 camera::RenderTarget, prelude::*, render::render_resource::TextureFormat,
2771 ui::widget::ViewportNode,
2772 };
2773
2774 pub fn setup(
2775 mut commands: Commands,
2776 mut images: ResMut<Assets<Image>>,
2777 mut meshes: ResMut<Assets<Mesh>>,
2778 mut materials: ResMut<Assets<StandardMaterial>>,
2779 ) {
2780 commands.spawn((
2781 Camera3d::default(),
2782 DespawnOnExit(super::Scene::ViewportNode),
2783 ));
2784
2785 let image = Image::new_target_texture(0, 0, TextureFormat::Bgra8UnormSrgb, None);
2786 let image_handle = images.add(image);
2787
2788 let camera = commands
2789 .spawn((
2790 Camera3d::default(),
2791 Camera {
2792 order: -1,
2793 ..default()
2794 },
2795 RenderTarget::Image(image_handle.into()),
2796 DespawnOnExit(super::Scene::ViewportNode),
2797 ))
2798 .id();
2799
2800 commands.spawn((
2801 Mesh3d(meshes.add(Cuboid::new(5.0, 5.0, 5.0))),
2802 MeshMaterial3d(materials.add(Color::WHITE)),
2803 Transform {
2804 translation: Vec3::new(0.0, 0.0, -10.0),
2805 rotation: Quat::from_euler(EulerRot::XYZ, 0.4, 0.7, 0.1),
2806 ..default()
2807 },
2808 DespawnOnExit(super::Scene::ViewportNode),
2809 ));
2810
2811 commands.spawn((
2812 Node {
2813 position_type: PositionType::Absolute,
2814 top: px(50),
2815 left: px(50),
2816 width: px(200),
2817 height: px(200),
2818 border: UiRect::all(px(5)),
2819 ..default()
2820 },
2821 BorderColor::all(Color::WHITE),
2822 ViewportNode::new(camera),
2823 DespawnOnExit(super::Scene::ViewportNode),
2824 ));
2825 }
2826}
2827
2828mod outer_color {
2829 use bevy::prelude::*;
2830
2831 pub fn setup(mut commands: Commands) {
2832 let radius = CornerRadius::from(percent(33.));
2833 let width = px(10.);
2834
2835 commands.spawn((Camera2d, DespawnOnExit(super::Scene::OuterColor)));
2836 commands
2837 .spawn((
2838 Node {
2839 display: Display::Grid,
2840 grid_template_columns: RepeatedGridTrack::px(3, 200.),
2841 grid_template_rows: RepeatedGridTrack::px(3, 200.),
2842 margin: UiRect::AUTO,
2843 ..default()
2844 },
2845 DespawnOnExit(super::Scene::OuterColor),
2846 ))
2847 .with_children(|builder| {
2848 for (border, border_radius, invert) in [
2849 (UiRect::ZERO, BorderRadius::bottom_right(radius), true),
2850 (UiRect::top(width), BorderRadius::top(radius), false),
2851 (UiRect::ZERO, BorderRadius::bottom_left(radius), true),
2852 (UiRect::left(width), BorderRadius::left(radius), false),
2853 (UiRect::all(width), BorderRadius::all(radius.x), true),
2854 (UiRect::right(width), BorderRadius::right(radius), false),
2855 (UiRect::ZERO, BorderRadius::top_right(radius), true),
2856 (UiRect::bottom(width), BorderRadius::bottom(radius), false),
2857 (UiRect::ZERO, BorderRadius::top_left(radius), true),
2858 ] {
2859 builder
2860 .spawn((
2861 Node {
2862 width: px(200.),
2863 height: px(200.),
2864 border_radius,
2865 border,
2866 ..default()
2867 },
2868 BorderColor::all(bevy::color::palettes::css::RED),
2869 ))
2870 .insert_if(BackgroundColor(Color::WHITE), || !invert)
2871 .insert_if(OuterColor(Color::WHITE), || invert);
2872 }
2873 });
2874 }
2875}
2876
2877mod boxed_content {
2878 use bevy::color::palettes::css::RED;
2879 use bevy::prelude::*;
2880
2881 pub fn setup(mut commands: Commands) {
2882 commands.spawn((Camera2d, DespawnOnExit(super::Scene::BoxedContent)));
2883 commands
2884 .spawn((
2885 Node {
2886 margin: auto().all(),
2887 column_gap: px(30),
2888 ..default()
2889 },
2890 DespawnOnExit(super::Scene::BoxedContent),
2891 ))
2892 .with_children(|builder| {
2893 for (heading, text_justify) in [
2894 ("Left", Justify::Left),
2895 ("Center", Justify::Center),
2896 ("Right", Justify::Right),
2897 ] {
2898 builder
2899 .spawn(Node {
2900 flex_direction: FlexDirection::Column,
2901 align_items: AlignItems::Center,
2902 justify_content: JustifyContent::Start,
2903 row_gap: px(20),
2904 ..default()
2905 })
2906 .with_children(|builder| {
2907 builder.spawn((
2908 Node::default(),
2909 Text::new(format!("{heading} justify")),
2910 TextFont::from_font_size(FontSize::Px(14.)),
2911 TextLayout::justify(Justify::Center),
2912 ));
2913
2914 builder.spawn((
2915 Node::default(),
2916 Text::new("This text has\nno border or padding."),
2917 TextFont::from_font_size(FontSize::Px(10.)),
2918 TextLayout::justify(text_justify),
2919 Outline {
2920 width: px(2),
2921 color: Color::WHITE,
2922 ..Default::default()
2923 },
2924 ));
2925
2926 builder.spawn((
2927 Node {
2928 border: px(10).all(),
2929 ..default()
2930 },
2931 Text::new("This text has\na border but no padding."),
2932 TextFont::from_font_size(FontSize::Px(10.)),
2933 TextLayout::justify(text_justify),
2934 BorderColor::all(RED),
2935 Outline {
2936 width: px(2),
2937 color: Color::WHITE,
2938 ..Default::default()
2939 },
2940 ));
2941
2942 builder.spawn((
2943 Node {
2944 padding: px(20).all(),
2945 ..default()
2946 },
2947 Text::new("This text has\npadding but no border."),
2948 TextFont::from_font_size(FontSize::Px(10.)),
2949 TextLayout::justify(text_justify),
2950 Outline {
2951 width: px(2),
2952 color: Color::WHITE,
2953 ..Default::default()
2954 },
2955 ));
2956
2957 builder.spawn((
2958 Node {
2959 border: px(10).all(),
2960 padding: px(20).all(),
2961 ..default()
2962 },
2963 Text::new("This text has\nborder and padding."),
2964 TextFont::from_font_size(FontSize::Px(10.)),
2965 TextLayout::justify(text_justify),
2966 BorderColor::all(RED),
2967 Outline {
2968 width: px(2),
2969 color: Color::WHITE,
2970 ..Default::default()
2971 },
2972 ));
2973
2974 builder.spawn((
2975 Node {
2976 border: px(10).left(),
2977 ..default()
2978 },
2979 Text::new("This text has\na left border and no padding."),
2980 TextFont::from_font_size(FontSize::Px(10.)),
2981 TextLayout::justify(text_justify),
2982 BorderColor::all(RED),
2983 Outline {
2984 width: px(2),
2985 color: Color::WHITE,
2986 ..Default::default()
2987 },
2988 ));
2989
2990 builder.spawn((
2991 Node {
2992 border: px(10).right(),
2993 ..default()
2994 },
2995 Text::new("This text has\na right border and no padding."),
2996 TextFont::from_font_size(FontSize::Px(10.)),
2997 TextLayout::justify(text_justify),
2998 BorderColor::all(RED),
2999 Outline {
3000 width: px(2),
3001 color: Color::WHITE,
3002 ..Default::default()
3003 },
3004 ));
3005
3006 builder.spawn((
3007 Node {
3008 padding: px(20).top().with_right(px(20)),
3009 ..default()
3010 },
3011 Text::new("This text has\npadding on its top and right."),
3012 TextFont::from_font_size(FontSize::Px(10.)),
3013 TextLayout::justify(text_justify),
3014 BorderColor::all(RED),
3015 Outline {
3016 width: px(2),
3017 color: Color::WHITE,
3018 ..Default::default()
3019 },
3020 ));
3021
3022 builder.spawn((
3023 Node {
3024 padding: px(20).bottom().with_left(px(20)),
3025 ..default()
3026 },
3027 Text::new("This text has\npadding on its bottom and left."),
3028 TextFont::from_font_size(FontSize::Px(10.)),
3029 TextLayout::justify(text_justify),
3030 BorderColor::all(RED),
3031 Outline {
3032 width: px(2),
3033 color: Color::WHITE,
3034 ..Default::default()
3035 },
3036 ));
3037
3038 builder.spawn((
3039 Node {
3040 padding: px(20).top().with_left(px(20)),
3041 border: px(10).bottom().with_right(px(10)),
3042 ..default()
3043 },
3044 Text::new(
3045 "This text has\npadding on its top and left\nand a border on its bottom and right.",
3046 ),
3047 TextFont::from_font_size(FontSize::Px(10.)),
3048 TextLayout::justify(text_justify),
3049 BorderColor::all(RED),
3050 Outline {
3051 width: px(2),
3052 color: Color::WHITE,
3053 ..Default::default()
3054 },
3055 ));
3056 });
3057 }
3058 });
3059 }
3060}
3061
3062mod editable_text {
3063 use bevy::color::palettes::css::YELLOW;
3064 use bevy::prelude::*;
3065 use bevy::text::EditableText;
3066 use bevy::text::TextCursorStyle;
3067 use bevy::text::TextEdit;
3068 use bevy::ui_widgets::TextInput;
3069
3070 const DUMMY_TEXT: &str = "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten";
3071 const LOREM_TEXT: &str = concat!(
3072 "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ",
3073 "Aenean commodo ligula eget dolor. Aenean massa. ",
3074 "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur reprehenderit mus. ",
3075 "Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. ",
3076 "Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. ",
3077 "In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. ",
3078 "Nullam dictum felis eu pede mollis pretium. Integer tincidunt. ",
3079 "Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. ",
3080 "Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. ",
3081 "Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. ",
3082 "Phasellus viverra nulla ut metus officia laoreet. Quisque rutrum. ",
3083 "Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi.",
3084 " Qui eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, ",
3085 "sem quam semper libero, sit amet adipiscing sem neque sed ipsum. ",
3086 "Qui quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. ",
3087 "Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. ",
3088 "Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. ",
3089 "Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. ",
3090 "Sed consequat, leo eget bibendum sodales, augue velit cursus nunc,"
3091 );
3092
3093 pub fn setup(mut commands: Commands) {
3094 commands.spawn((Camera2d, DespawnOnExit(super::Scene::EditableText)));
3095 commands.spawn((
3096 Node {
3097 flex_wrap: FlexWrap::Wrap,
3098 align_items: AlignItems::Start,
3099 margin: px(10.).all(),
3100 width: vw(100),
3101 height: vh(100),
3102 row_gap: px(10),
3103 column_gap: px(20),
3104 ..default()
3105 },
3106 DespawnOnExit(super::Scene::EditableText),
3107 children![
3108 (
3109 Node {
3110 flex_direction: FlexDirection::Column,
3111 row_gap: px(10),
3112 ..default()
3113 },
3114 children![
3115 Text::new("Single line"),
3116 (
3117 TextInput,
3118 EditableText {
3119 pending_edits: vec![TextEdit::Insert(
3120 "Single line EditableText".into(),
3121 )],
3122 ..default()
3123 },
3124 TextLayout::no_wrap(),
3125 TextFont {
3126 font_size: FontSize::Px(10.),
3127 ..default()
3128 },
3129 TextCursorStyle::default(),
3130 Node {
3131 width: px(200.),
3132 border: px(2).all(),
3133 ..default()
3134 },
3135 BorderColor::all(YELLOW),
3136 ),
3137 Text::new("Initial end"),
3138 (
3139 EditableText::new(LOREM_TEXT),
3140 TextLayout::no_wrap(),
3141 TextFont {
3142 font_size: FontSize::Px(10.),
3143 ..default()
3144 },
3145 TextCursorStyle::default(),
3146 Node {
3147 width: px(200.),
3148 border: px(2).all(),
3149 ..default()
3150 },
3151 BorderColor::all(YELLOW),
3152 ),
3153 Text::new("Insert end"),
3154 (
3155 TextInput,
3156 EditableText {
3157 pending_edits: vec![TextEdit::Insert(LOREM_TEXT.into())],
3158 ..default()
3159 },
3160 TextLayout::no_wrap(),
3161 TextFont {
3162 font_size: FontSize::Px(10.),
3163 ..default()
3164 },
3165 TextCursorStyle::default(),
3166 Node {
3167 width: px(200.),
3168 border: px(2).all(),
3169 ..default()
3170 },
3171 BorderColor::all(YELLOW),
3172 ),
3173 Text::new("Select line start"),
3174 (
3175 TextInput,
3176 EditableText {
3177 pending_edits: vec![
3178 TextEdit::Insert(LOREM_TEXT.into()),
3179 TextEdit::LineStart(true),
3180 ],
3181 ..default()
3182 },
3183 TextLayout::no_wrap(),
3184 TextFont {
3185 font_size: FontSize::Px(10.),
3186 ..default()
3187 },
3188 TextCursorStyle::default(),
3189 Node {
3190 width: px(200.),
3191 border: px(2).all(),
3192 ..default()
3193 },
3194 BorderColor::all(YELLOW),
3195 ),
3196 ],
3197 ),
3198 (
3199 Node {
3200 flex_direction: FlexDirection::Column,
3201 row_gap: px(10),
3202 ..default()
3203 },
3204 children![
3205 Text::new("Wrapped start"),
3206 (
3207 TextInput,
3208 EditableText {
3209 pending_edits: vec![
3210 TextEdit::Insert(LOREM_TEXT.into()),
3211 TextEdit::TextStart(false),
3212 ],
3213 visible_lines: Some(8.),
3214 ..default()
3215 },
3216 Node {
3217 width: px(200.),
3218 border: px(2).all(),
3219 ..default()
3220 },
3221 TextCursorStyle::default(),
3222 TextFont {
3223 font_size: FontSize::Px(10.),
3224 ..default()
3225 },
3226 BorderColor::all(YELLOW),
3227 ),
3228 ],
3229 ),
3230 (
3231 Node {
3232 flex_direction: FlexDirection::Column,
3233 row_gap: px(10),
3234 ..default()
3235 },
3236 children![
3237 Text::new("Wrapped selection"),
3238 (
3239 TextInput,
3240 EditableText {
3241 pending_edits: vec![
3242 TextEdit::Insert(LOREM_TEXT.into()),
3243 TextEdit::TextStart(false),
3244 TextEdit::Down(false),
3245 TextEdit::TextEnd(true),
3246 ],
3247 visible_lines: Some(8.),
3248 ..default()
3249 },
3250 TextCursorStyle::default(),
3251 Node {
3252 width: px(200.),
3253 border: px(2).all(),
3254 ..default()
3255 },
3256 TextFont {
3257 font_size: FontSize::Px(10.),
3258 ..default()
3259 },
3260 BorderColor::all(YELLOW),
3261 ),
3262 ],
3263 ),
3264 (
3265 Node {
3266 flex_direction: FlexDirection::Column,
3267 row_gap: px(10),
3268 ..default()
3269 },
3270 children![
3271 Text::new("Clamp top"),
3272 (
3273 TextInput,
3274 EditableText {
3275 pending_edits: vec![
3276 TextEdit::Insert(DUMMY_TEXT.into()),
3277 TextEdit::ScrollByLines(-10.0),
3278 ],
3279 visible_lines: Some(5.5),
3280 ..default()
3281 },
3282 TextCursorStyle::default(),
3283 TextFont {
3284 font_size: FontSize::Px(10.),
3285 ..default()
3286 },
3287 Node {
3288 width: px(100.),
3289 border: px(2).all(),
3290 ..default()
3291 },
3292 BorderColor::all(YELLOW),
3293 ),
3294 ],
3295 ),
3296 (
3297 Node {
3298 flex_direction: FlexDirection::Column,
3299 row_gap: px(10),
3300 ..default()
3301 },
3302 children![
3303 Text::new("Home, Scroll 1"),
3304 (
3305 TextInput,
3306 EditableText {
3307 pending_edits: vec![
3308 TextEdit::Insert(DUMMY_TEXT.into()),
3309 TextEdit::ScrollTo(Vec2::ZERO),
3310 TextEdit::ScrollByLines(1.0),
3311 ],
3312 visible_lines: Some(5.5),
3313 ..default()
3314 },
3315 TextCursorStyle::default(),
3316 TextFont {
3317 font_size: FontSize::Px(10.),
3318 ..default()
3319 },
3320 Node {
3321 width: px(100.),
3322 border: px(2).all(),
3323 ..default()
3324 },
3325 BorderColor::all(YELLOW),
3326 ),
3327 ],
3328 ),
3329 (
3330 Node {
3331 flex_direction: FlexDirection::Column,
3332 row_gap: px(10),
3333 ..default()
3334 },
3335 children![
3336 Text::new("Home, Scroll 2"),
3337 (
3338 TextInput,
3339 EditableText {
3340 pending_edits: vec![
3341 TextEdit::Insert(DUMMY_TEXT.into()),
3342 TextEdit::ScrollTo(Vec2::ZERO),
3343 TextEdit::ScrollByLines(2.0),
3344 ],
3345 visible_lines: Some(5.5),
3346 ..default()
3347 },
3348 TextFont {
3349 font_size: FontSize::Px(10.),
3350 ..default()
3351 },
3352 TextCursorStyle::default(),
3353 Node {
3354 width: px(100.),
3355 border: px(2).all(),
3356 ..default()
3357 },
3358 BorderColor::all(YELLOW),
3359 ),
3360 ],
3361 ),
3362 (
3363 Node {
3364 flex_direction: FlexDirection::Column,
3365 row_gap: px(10),
3366 ..default()
3367 },
3368 children![
3369 Text::new("Clamp bottom"),
3370 (
3371 TextInput,
3372 EditableText {
3373 pending_edits: vec![
3374 TextEdit::Insert(DUMMY_TEXT.into()),
3375 TextEdit::ScrollByLines(-1000.0),
3376 TextEdit::ScrollByLines(1000.0),
3377 ],
3378 visible_lines: Some(5.5),
3379 ..default()
3380 },
3381 TextCursorStyle::default(),
3382 TextFont {
3383 font_size: FontSize::Px(10.),
3384 ..default()
3385 },
3386 Node {
3387 width: px(100.),
3388 border: px(2).all(),
3389 ..default()
3390 },
3391 BorderColor::all(YELLOW),
3392 ),
3393 ],
3394 ),
3395 (
3396 Node {
3397 flex_direction: FlexDirection::Column,
3398 row_gap: px(10),
3399 ..default()
3400 },
3401 children![
3402 Text::new("Bottom -1"),
3403 (
3404 TextInput,
3405 EditableText {
3406 pending_edits: vec![
3407 TextEdit::Insert(DUMMY_TEXT.into()),
3408 TextEdit::ScrollByLines(-1000.0),
3409 TextEdit::ScrollByLines(1000.0),
3410 TextEdit::ScrollByLines(-1.0),
3411 ],
3412 visible_lines: Some(5.5),
3413 ..default()
3414 },
3415 TextCursorStyle::default(),
3416 TextFont {
3417 font_size: FontSize::Px(10.),
3418 ..default()
3419 },
3420 Node {
3421 width: px(100.),
3422 border: px(2).all(),
3423 ..default()
3424 },
3425 BorderColor::all(YELLOW),
3426 ),
3427 ],
3428 ),
3429 (
3430 Node {
3431 flex_direction: FlexDirection::Column,
3432 row_gap: px(10),
3433 ..default()
3434 },
3435 children![
3436 Text::new("Top +3"),
3437 (
3438 TextInput,
3439 EditableText {
3440 pending_edits: vec![
3441 TextEdit::Insert(DUMMY_TEXT.into()),
3442 TextEdit::ScrollByLines(-1000.0),
3443 TextEdit::ScrollByLines(3.0),
3444 ],
3445 visible_lines: Some(5.5),
3446 ..default()
3447 },
3448 TextCursorStyle::default(),
3449 TextFont {
3450 font_size: FontSize::Px(10.),
3451 ..default()
3452 },
3453 Node {
3454 width: px(100.),
3455 border: px(2).all(),
3456 ..default()
3457 },
3458 BorderColor::all(YELLOW),
3459 ),
3460 ],
3461 ),
3462 (
3463 Node {
3464 flex_direction: FlexDirection::Column,
3465 row_gap: px(10),
3466 ..default()
3467 },
3468 children![
3469 Text::new("Select down 3"),
3470 (
3471 TextInput,
3472 EditableText {
3473 pending_edits: vec![
3474 TextEdit::Insert(DUMMY_TEXT.into()),
3475 TextEdit::TextStart(false),
3476 TextEdit::Down(false),
3477 TextEdit::Down(true),
3478 TextEdit::Down(true),
3479 TextEdit::Down(true),
3480 ],
3481 visible_lines: Some(5.5),
3482 ..default()
3483 },
3484 TextCursorStyle::default(),
3485 TextFont {
3486 font_size: FontSize::Px(10.),
3487 ..default()
3488 },
3489 Node {
3490 width: px(100.),
3491 border: px(2).all(),
3492 ..default()
3493 },
3494 BorderColor::all(YELLOW),
3495 ),
3496 ],
3497 ),
3498 (
3499 Node {
3500 flex_direction: FlexDirection::Column,
3501 row_gap: px(10),
3502 ..default()
3503 },
3504 children![
3505 Text::new("End, Scroll 1"),
3506 (
3507 TextInput,
3508 EditableText {
3509 pending_edits: vec![
3510 TextEdit::Insert(DUMMY_TEXT.into()),
3511 TextEdit::ScrollByLines(1.0),
3512 ],
3513 visible_lines: Some(5.5),
3514 ..default()
3515 },
3516 TextCursorStyle::default(),
3517 TextFont {
3518 font_size: FontSize::Px(10.),
3519 ..default()
3520 },
3521 Node {
3522 width: px(100.),
3523 border: px(2).all(),
3524 ..default()
3525 },
3526 BorderColor::all(YELLOW),
3527 ),
3528 ],
3529 ),
3530 (
3531 Node {
3532 flex_direction: FlexDirection::Column,
3533 row_gap: px(10),
3534 ..default()
3535 },
3536 children![
3537 Text::new("End, Scroll -0.5"),
3538 (
3539 TextInput,
3540 EditableText {
3541 pending_edits: vec![
3542 TextEdit::Insert(DUMMY_TEXT.into()),
3543 TextEdit::ScrollByLines(-0.5),
3544 ],
3545 visible_lines: Some(5.5),
3546 ..default()
3547 },
3548 TextCursorStyle::default(),
3549 TextFont {
3550 font_size: FontSize::Px(10.),
3551 ..default()
3552 },
3553 Node {
3554 width: px(100.),
3555 border: px(2).all(),
3556 ..default()
3557 },
3558 BorderColor::all(YELLOW),
3559 ),
3560 ],
3561 )
3562 ],
3563 ));
3564 }
3565}
3566
3567mod node_material {
3568 use bevy::{
3569 color::palettes::tailwind::{CYAN_100, RED_500, YELLOW_100},
3570 prelude::*,
3571 render::render_resource::AsBindGroup,
3572 shader::ShaderRef,
3573 };
3574 const SHADER_ASSET_PATH: &str = "shaders/custom_ui_material.wesl";
3575
3576 #[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
3577 pub(super) struct DefaultUiMaterial {}
3578
3579 impl UiMaterial for DefaultUiMaterial {}
3580
3581 #[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
3582 pub(super) struct CustomUiMaterial {
3583 /// Color multiplied with the image
3584 #[uniform(0)]
3585 color: Vec4,
3586 /// Represents how much of the image is visible
3587 /// Goes from 0 to 1
3588 /// A `Vec4` is used here because Bevy with webgl2 requires that uniforms are 16-byte aligned but only the first component is read.
3589 #[uniform(1)]
3590 slider: Vec4,
3591 /// Image used to represent the slider
3592 #[texture(2)]
3593 #[sampler(3)]
3594 color_texture: Handle<Image>,
3595 /// Color of the image's border
3596 #[uniform(4)]
3597 border_color: Vec4,
3598 }
3599
3600 impl UiMaterial for CustomUiMaterial {
3601 fn fragment_shader() -> ShaderRef {
3602 SHADER_ASSET_PATH.into()
3603 }
3604 }
3605
3606 pub fn setup(
3607 mut commands: Commands,
3608 mut default_ui_materials: ResMut<Assets<DefaultUiMaterial>>,
3609 mut ui_materials: ResMut<Assets<CustomUiMaterial>>,
3610 asset_server: Res<AssetServer>,
3611 ) {
3612 let default_material = default_ui_materials.add(DefaultUiMaterial {});
3613 let custom_material = |slider| CustomUiMaterial {
3614 color: LinearRgba::from(CYAN_100).to_vec4(),
3615 slider: Vec4::splat(slider),
3616 color_texture: asset_server.load("branding/banner.png"),
3617 border_color: LinearRgba::from(YELLOW_100).to_vec4(),
3618 };
3619 let full_material = ui_materials.add(custom_material(1.));
3620 let material_node = Node {
3621 width: px(200),
3622 height: px(80),
3623 border: px(10).all(),
3624 border_radius: BorderRadius::all(px(20)),
3625 ..default()
3626 };
3627 let material_label = |text: &str| (Text::new(text), TextFont::from_font_size(px(10.)));
3628
3629 commands.spawn((Camera2d, DespawnOnExit(super::Scene::NodeMaterial)));
3630 commands.spawn((
3631 Node {
3632 display: Display::Grid,
3633 // two column grid, labels then materials, first row is spanned by title.
3634 width: percent(100),
3635 height: percent(100),
3636 grid_template_columns: vec![GridTrack::auto(), GridTrack::px(200.)],
3637 align_items: AlignItems::Center,
3638 align_content: AlignContent::Center,
3639 justify_content: JustifyContent::Center,
3640 row_gap: px(10.),
3641 column_gap: px(20.),
3642 ..default()
3643 },
3644 DespawnOnExit(super::Scene::NodeMaterial),
3645 children![
3646 (
3647 Text::new("MaterialNode"),
3648 TextFont::from_font_size(px(30.)),
3649 TextLayout::justify(Justify::Center),
3650 Node {
3651 grid_column: GridPlacement::span(2),
3652 margin: px(10.).bottom(),
3653 ..default()
3654 },
3655 ),
3656 // Default UI material (just white with the default ui_material.wgsl shader for the bevy_ui crate)
3657 material_label("'ui_material.wgsl' default material"),
3658 (material_node.clone(), MaterialNode(default_material),),
3659 // Custom UI material, 1./3 full.
3660 material_label("'custom_ui_material.wgsl' 1/3"),
3661 (
3662 material_node.clone(),
3663 MaterialNode(ui_materials.add(custom_material(1. / 3.))),
3664 ),
3665 // Custom UI material, 2./3 full.
3666 material_label("'custom_ui_material.wgsl' 2/3"),
3667 (
3668 material_node.clone(),
3669 MaterialNode(ui_materials.add(custom_material(2. / 3.)))
3670 ),
3671 // Custom UI material, full.
3672 material_label("'custom_ui_material.wgsl' Full"),
3673 (material_node.clone(), MaterialNode(full_material.clone()),),
3674 // Custom UI material, full. Clipped using overflow so bottom 50% missing
3675 material_label("'custom_ui_material.wgsl' clipped vertically"),
3676 (
3677 Node {
3678 width: material_node.width,
3679 height: material_node.height,
3680 ..default()
3681 },
3682 Outline {
3683 width: px(1),
3684 color: RED_500.into(),
3685 ..default()
3686 },
3687 children![(
3688 Node {
3689 width: percent(100),
3690 height: percent(50),
3691 overflow: Overflow::clip(),
3692 ..default()
3693 },
3694 children![(material_node.clone(), MaterialNode(full_material.clone()))],
3695 )],
3696 ),
3697 // Custom UI material clipped on both axis so bottom 25% and right 25% not visible.
3698 material_label("'custom_ui_material.wgsl' end clipped on both axis"),
3699 (
3700 Node {
3701 width: material_node.width,
3702 height: material_node.height,
3703 ..default()
3704 },
3705 Outline {
3706 width: px(1),
3707 color: RED_500.into(),
3708 ..default()
3709 },
3710 children![(
3711 Node {
3712 width: percent(75),
3713 height: percent(75),
3714 overflow: Overflow::clip(),
3715 ..default()
3716 },
3717 children![(
3718 Node {
3719 position_type: PositionType::Absolute,
3720 width: px(200),
3721 height: px(80),
3722 border: px(10).all(),
3723 border_radius: BorderRadius::all(px(20)),
3724 ..default()
3725 },
3726 MaterialNode(full_material.clone()),
3727 )],
3728 )],
3729 ),
3730 // Custom UI material clipped on both axis so top 25% and left 25% not visible.
3731 material_label("'custom_ui_material.wgsl' start clipped on both axis"),
3732 (
3733 Node {
3734 width: material_node.width,
3735 height: material_node.height,
3736 align_items: AlignItems::End,
3737 justify_content: JustifyContent::End,
3738 ..default()
3739 },
3740 Outline {
3741 width: px(1),
3742 color: RED_500.into(),
3743 ..default()
3744 },
3745 children![(
3746 Node {
3747 width: percent(75),
3748 height: percent(75),
3749 overflow: Overflow::clip(),
3750 ..default()
3751 },
3752 children![(
3753 Node {
3754 position_type: PositionType::Absolute,
3755 width: px(200),
3756 height: px(80),
3757 right: px(0),
3758 bottom: px(0),
3759 border: px(10).all(),
3760 border_radius: BorderRadius::all(px(20)),
3761 ..default()
3762 },
3763 MaterialNode(full_material),
3764 )],
3765 )],
3766 ),
3767 ],
3768 ));
3769 }More examples
18fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
19 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
20 commands.spawn(Camera2d);
21
22 // Top-level grid (app frame)
23 commands
24 .spawn((
25 Node {
26 // Use the CSS Grid algorithm for laying out this node
27 display: Display::Grid,
28 // Make node fill the entirety of its parent (in this case the window)
29 width: percent(100),
30 height: percent(100),
31 // Set the grid to have 2 columns with sizes [min-content, minmax(0, 1fr)]
32 // - The first column will size to the size of its contents
33 // - The second column will take up the remaining available space
34 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
35 // Set the grid to have 3 rows with sizes [auto, minmax(0, 1fr), 20px]
36 // - The first row will size to the size of its contents
37 // - The second row take up remaining available space (after rows 1 and 3 have both been sized)
38 // - The third row will be exactly 20px high
39 grid_template_rows: vec![
40 GridTrack::auto(),
41 GridTrack::flex(1.0),
42 GridTrack::px(20.),
43 ],
44 ..default()
45 },
46 BackgroundColor(Color::WHITE),
47 ))
48 .with_children(|builder| {
49 // Header
50 builder
51 .spawn(
52 Node {
53 display: Display::Grid,
54 // Make this node span two grid columns so that it takes up the entire top tow
55 grid_column: GridPlacement::span(2),
56 padding: UiRect::all(px(6)),
57 ..default()
58 },
59 )
60 .with_children(|builder| {
61 spawn_nested_text_bundle(builder, font.clone(), "Bevy CSS Grid Layout Example");
62 });
63
64 // Main content grid (auto placed in row 2, column 1)
65 builder
66 .spawn((
67 Node {
68 // Make the height of the node fill its parent
69 height: percent(100),
70 // Make the grid have a 1:1 aspect ratio meaning it will scale as an exact square
71 // As the height is set explicitly, this means the width will adjust to match the height
72 aspect_ratio: Some(1.0),
73 // Use grid layout for this node
74 display: Display::Grid,
75 // Add 24px of padding around the grid
76 padding: UiRect::all(px(24)),
77 // Set the grid to have 4 columns all with sizes minmax(0, 1fr)
78 // This creates 4 exactly evenly sized columns
79 grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
80 // Set the grid to have 4 rows all with sizes minmax(0, 1fr)
81 // This creates 4 exactly evenly sized rows
82 grid_template_rows: RepeatedGridTrack::flex(4, 1.0),
83 // Set a 12px gap/gutter between rows and columns
84 row_gap: px(12),
85 column_gap: px(12),
86 ..default()
87 },
88 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
89 ))
90 .with_children(|builder| {
91 // Note there is no need to specify the position for each grid item. Grid items that are
92 // not given an explicit position will be automatically positioned into the next available
93 // grid cell. The order in which this is performed can be controlled using the grid_auto_flow
94 // style property.
95
96 item_rect(builder, ORANGE);
97 item_rect(builder, BISQUE);
98 item_rect(builder, BLUE);
99 item_rect(builder, CRIMSON);
100 item_rect(builder, AQUA);
101 item_rect(builder, ORANGE_RED);
102 item_rect(builder, DARK_GREEN);
103 item_rect(builder, FUCHSIA);
104 item_rect(builder, TEAL);
105 item_rect(builder, ALICE_BLUE);
106 item_rect(builder, CRIMSON);
107 item_rect(builder, ANTIQUE_WHITE);
108 item_rect(builder, YELLOW);
109 item_rect(builder, DEEP_PINK);
110 item_rect(builder, YELLOW_GREEN);
111 item_rect(builder, SALMON);
112 });
113
114 // Right side bar (auto placed in row 2, column 2)
115 builder
116 .spawn((
117 Node {
118 display: Display::Grid,
119 // Align content towards the start (top) in the vertical axis
120 align_items: AlignItems::Start,
121 // Align content towards the center in the horizontal axis
122 justify_items: JustifyItems::Center,
123 // Add 10px padding
124 padding: UiRect::all(px(10)),
125 // Add an fr track to take up all the available space at the bottom of the column so that the text nodes
126 // can be top-aligned. Normally you'd use flexbox for this, but this is the CSS Grid example so we're using grid.
127 grid_template_rows: vec![GridTrack::auto(), GridTrack::auto(), GridTrack::fr(1.0)],
128 // Add a 10px gap between rows
129 row_gap: px(10),
130 ..default()
131 },
132 BackgroundColor(BLACK.into()),
133 ))
134 .with_children(|builder| {
135 builder.spawn((Text::new("Sidebar"),
136 TextFont::from(font.clone()),
137 ));
138 builder.spawn((Text::new("A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely."),
139 TextFont {
140 font: font.clone().into(),
141 font_size: FontSize::Px(13.0),
142 ..default()
143 },
144 ));
145 builder.spawn(Node::default());
146 });
147
148 // Footer / status bar
149 builder.spawn((
150 Node {
151 // Make this node span two grid column so that it takes up the entire bottom row
152 grid_column: GridPlacement::span(2),
153 ..default()
154 },
155 BackgroundColor(WHITE.into()),
156 ));
157
158 // Modal (absolutely positioned on top of content - currently hidden: to view it, change its visibility)
159 builder.spawn((
160 Node {
161 position_type: PositionType::Absolute,
162 margin: UiRect {
163 top: px(100),
164 bottom: auto(),
165 left: auto(),
166 right: auto(),
167 },
168 width: percent(60),
169 height: px(300),
170 max_width: px(600),
171 ..default()
172 },
173 Visibility::Hidden,
174 BackgroundColor(Color::WHITE.with_alpha(0.8)),
175 ));
176 });
177}Sourcepub fn min_content<T>() -> T
pub fn min_content<T>() -> T
Create a grid track which is automatically sized to fit its contents when sized at their “min-content” sizes
Examples found in repository?
1135 pub fn setup(mut commands: Commands) {
1136 commands.spawn((Camera2d, DespawnOnExit(super::Scene::Grid)));
1137 // Top-level grid (app frame)
1138 commands.spawn((
1139 Node {
1140 display: Display::Grid,
1141 width: percent(100),
1142 height: percent(100),
1143 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
1144 grid_template_rows: vec![
1145 GridTrack::auto(),
1146 GridTrack::flex(1.0),
1147 GridTrack::px(40.),
1148 ],
1149 ..default()
1150 },
1151 BackgroundColor(Color::WHITE),
1152 DespawnOnExit(super::Scene::Grid),
1153 children![
1154 // Header
1155 (
1156 Node {
1157 display: Display::Grid,
1158 grid_column: GridPlacement::span(2),
1159 padding: UiRect::all(px(40)),
1160 ..default()
1161 },
1162 BackgroundColor(RED.into()),
1163 ),
1164 // Main content grid (auto placed in row 2, column 1)
1165 (
1166 Node {
1167 height: percent(100),
1168 aspect_ratio: Some(1.0),
1169 display: Display::Grid,
1170 grid_template_columns: RepeatedGridTrack::flex(3, 1.0),
1171 grid_template_rows: RepeatedGridTrack::flex(2, 1.0),
1172 row_gap: px(12),
1173 column_gap: px(12),
1174 ..default()
1175 },
1176 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
1177 children![
1178 (Node::default(), BackgroundColor(ORANGE.into())),
1179 (Node::default(), BackgroundColor(BISQUE.into())),
1180 (Node::default(), BackgroundColor(BLUE.into())),
1181 (Node::default(), BackgroundColor(CRIMSON.into())),
1182 (Node::default(), BackgroundColor(AQUA.into())),
1183 ]
1184 ),
1185 // Right side bar (auto placed in row 2, column 2)
1186 (Node::DEFAULT, BackgroundColor(BLACK.into())),
1187 ],
1188 ));
1189 }More examples
62fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
63 let image_handle = asset_server.load("branding/icon.png");
64 let full_text = format!(
65 "{}height : {}%, width : {}%",
66 TEXT_PREFIX, IMAGE_GROUP_BOX_INIT_HEIGHT, IMAGE_GROUP_BOX_INIT_WIDTH,
67 );
68
69 commands.spawn(Camera2d);
70
71 let container = commands
72 .spawn((
73 Node {
74 display: Display::Grid,
75 width: percent(100),
76 height: percent(100),
77 grid_template_rows: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
78 ..default()
79 },
80 BackgroundColor(Color::WHITE),
81 ))
82 .id();
83
84 // Keyboard Text
85 commands
86 .spawn((
87 TextData {
88 height: IMAGE_GROUP_BOX_INIT_HEIGHT,
89 width: IMAGE_GROUP_BOX_INIT_WIDTH,
90 },
91 Text::new(full_text),
92 TextColor::BLACK,
93 Node {
94 grid_row: GridPlacement::span(1),
95 padding: px(6).all(),
96 ..default()
97 },
98 UiDebugOptions {
99 enabled: false,
100 ..default()
101 },
102 ChildOf(container),
103 ))
104 .observe(update_text);
105
106 commands
107 .spawn((
108 Node {
109 display: Display::Flex,
110 grid_row: GridPlacement::span(1),
111 flex_direction: FlexDirection::Column,
112 justify_content: JustifyContent::SpaceAround,
113 padding: px(10.).all(),
114 ..default()
115 },
116 BackgroundColor(Color::BLACK),
117 ChildOf(container),
118 ))
119 .with_children(|builder| {
120 // `NodeImageMode::Auto` will resize the image automatically by taking the size of the source image and applying any layout constraints.
121 builder
122 .spawn((
123 ImageGroup,
124 Node {
125 display: Display::Flex,
126 justify_content: JustifyContent::Start,
127 width: percent(IMAGE_GROUP_BOX_INIT_WIDTH),
128 height: percent(IMAGE_GROUP_BOX_INIT_HEIGHT),
129 ..default()
130 },
131 BackgroundColor(Color::from(tailwind::BLUE_100)),
132 ))
133 .with_children(|parent| {
134 for _ in 0..4 {
135 // child node will apply Flex layout
136 parent.spawn((
137 Node::default(),
138 ImageNode {
139 image: image_handle.clone(),
140 image_mode: NodeImageMode::Auto,
141 ..default()
142 },
143 ));
144 }
145 });
146 // `NodeImageMode::Stretch` will resize the image to match the size of the `Node` component
147 builder
148 .spawn((
149 ImageGroup,
150 Node {
151 display: Display::Flex,
152 justify_content: JustifyContent::Start,
153 width: percent(IMAGE_GROUP_BOX_INIT_WIDTH),
154 height: percent(IMAGE_GROUP_BOX_INIT_HEIGHT),
155 ..default()
156 },
157 BackgroundColor(Color::from(tailwind::BLUE_100)),
158 ))
159 .with_children(|parent| {
160 for width in [10., 20., 30., 40.] {
161 parent.spawn((
162 Node {
163 height: percent(100),
164 width: percent(width),
165 ..default()
166 },
167 ImageNode {
168 image: image_handle.clone(),
169 image_mode: NodeImageMode::Stretch,
170 ..default()
171 },
172 ));
173 }
174 });
175 });
176}18fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
19 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
20 commands.spawn(Camera2d);
21
22 // Top-level grid (app frame)
23 commands
24 .spawn((
25 Node {
26 // Use the CSS Grid algorithm for laying out this node
27 display: Display::Grid,
28 // Make node fill the entirety of its parent (in this case the window)
29 width: percent(100),
30 height: percent(100),
31 // Set the grid to have 2 columns with sizes [min-content, minmax(0, 1fr)]
32 // - The first column will size to the size of its contents
33 // - The second column will take up the remaining available space
34 grid_template_columns: vec![GridTrack::min_content(), GridTrack::flex(1.0)],
35 // Set the grid to have 3 rows with sizes [auto, minmax(0, 1fr), 20px]
36 // - The first row will size to the size of its contents
37 // - The second row take up remaining available space (after rows 1 and 3 have both been sized)
38 // - The third row will be exactly 20px high
39 grid_template_rows: vec![
40 GridTrack::auto(),
41 GridTrack::flex(1.0),
42 GridTrack::px(20.),
43 ],
44 ..default()
45 },
46 BackgroundColor(Color::WHITE),
47 ))
48 .with_children(|builder| {
49 // Header
50 builder
51 .spawn(
52 Node {
53 display: Display::Grid,
54 // Make this node span two grid columns so that it takes up the entire top tow
55 grid_column: GridPlacement::span(2),
56 padding: UiRect::all(px(6)),
57 ..default()
58 },
59 )
60 .with_children(|builder| {
61 spawn_nested_text_bundle(builder, font.clone(), "Bevy CSS Grid Layout Example");
62 });
63
64 // Main content grid (auto placed in row 2, column 1)
65 builder
66 .spawn((
67 Node {
68 // Make the height of the node fill its parent
69 height: percent(100),
70 // Make the grid have a 1:1 aspect ratio meaning it will scale as an exact square
71 // As the height is set explicitly, this means the width will adjust to match the height
72 aspect_ratio: Some(1.0),
73 // Use grid layout for this node
74 display: Display::Grid,
75 // Add 24px of padding around the grid
76 padding: UiRect::all(px(24)),
77 // Set the grid to have 4 columns all with sizes minmax(0, 1fr)
78 // This creates 4 exactly evenly sized columns
79 grid_template_columns: RepeatedGridTrack::flex(4, 1.0),
80 // Set the grid to have 4 rows all with sizes minmax(0, 1fr)
81 // This creates 4 exactly evenly sized rows
82 grid_template_rows: RepeatedGridTrack::flex(4, 1.0),
83 // Set a 12px gap/gutter between rows and columns
84 row_gap: px(12),
85 column_gap: px(12),
86 ..default()
87 },
88 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
89 ))
90 .with_children(|builder| {
91 // Note there is no need to specify the position for each grid item. Grid items that are
92 // not given an explicit position will be automatically positioned into the next available
93 // grid cell. The order in which this is performed can be controlled using the grid_auto_flow
94 // style property.
95
96 item_rect(builder, ORANGE);
97 item_rect(builder, BISQUE);
98 item_rect(builder, BLUE);
99 item_rect(builder, CRIMSON);
100 item_rect(builder, AQUA);
101 item_rect(builder, ORANGE_RED);
102 item_rect(builder, DARK_GREEN);
103 item_rect(builder, FUCHSIA);
104 item_rect(builder, TEAL);
105 item_rect(builder, ALICE_BLUE);
106 item_rect(builder, CRIMSON);
107 item_rect(builder, ANTIQUE_WHITE);
108 item_rect(builder, YELLOW);
109 item_rect(builder, DEEP_PINK);
110 item_rect(builder, YELLOW_GREEN);
111 item_rect(builder, SALMON);
112 });
113
114 // Right side bar (auto placed in row 2, column 2)
115 builder
116 .spawn((
117 Node {
118 display: Display::Grid,
119 // Align content towards the start (top) in the vertical axis
120 align_items: AlignItems::Start,
121 // Align content towards the center in the horizontal axis
122 justify_items: JustifyItems::Center,
123 // Add 10px padding
124 padding: UiRect::all(px(10)),
125 // Add an fr track to take up all the available space at the bottom of the column so that the text nodes
126 // can be top-aligned. Normally you'd use flexbox for this, but this is the CSS Grid example so we're using grid.
127 grid_template_rows: vec![GridTrack::auto(), GridTrack::auto(), GridTrack::fr(1.0)],
128 // Add a 10px gap between rows
129 row_gap: px(10),
130 ..default()
131 },
132 BackgroundColor(BLACK.into()),
133 ))
134 .with_children(|builder| {
135 builder.spawn((Text::new("Sidebar"),
136 TextFont::from(font.clone()),
137 ));
138 builder.spawn((Text::new("A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely."),
139 TextFont {
140 font: font.clone().into(),
141 font_size: FontSize::Px(13.0),
142 ..default()
143 },
144 ));
145 builder.spawn(Node::default());
146 });
147
148 // Footer / status bar
149 builder.spawn((
150 Node {
151 // Make this node span two grid column so that it takes up the entire bottom row
152 grid_column: GridPlacement::span(2),
153 ..default()
154 },
155 BackgroundColor(WHITE.into()),
156 ));
157
158 // Modal (absolutely positioned on top of content - currently hidden: to view it, change its visibility)
159 builder.spawn((
160 Node {
161 position_type: PositionType::Absolute,
162 margin: UiRect {
163 top: px(100),
164 bottom: auto(),
165 left: auto(),
166 right: auto(),
167 },
168 width: percent(60),
169 height: px(300),
170 max_width: px(600),
171 ..default()
172 },
173 Visibility::Hidden,
174 BackgroundColor(Color::WHITE.with_alpha(0.8)),
175 ));
176 });
177}Sourcepub fn max_content<T>() -> T
pub fn max_content<T>() -> T
Create a grid track which is automatically sized to fit its contents when sized at their “max-content” sizes
Sourcepub fn fit_content_px<T>(limit: f32) -> T
pub fn fit_content_px<T>(limit: f32) -> T
Create a fit-content() grid track with fixed pixel limit.
https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content_function
Sourcepub fn fit_content_percent<T>(limit: f32) -> T
pub fn fit_content_percent<T>(limit: f32) -> T
Create a fit-content() grid track with percentage limit.
https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content_function
Sourcepub fn minmax<T>(min: MinTrackSizingFunction, max: MaxTrackSizingFunction) -> T
pub fn minmax<T>(min: MinTrackSizingFunction, max: MaxTrackSizingFunction) -> T
Create a minmax() grid track.
Sourcepub fn vmin<T>(value: f32) -> T
pub fn vmin<T>(value: f32) -> T
Create a grid track with a percentage of the viewport’s smaller dimension
Sourcepub fn vmax<T>(value: f32) -> T
pub fn vmax<T>(value: f32) -> T
Create a grid track with a percentage of the viewport’s larger dimension
Trait Implementations§
impl Copy for GridTrack
Source§impl<'de> Deserialize<'de> for GridTrack
impl<'de> Deserialize<'de> for GridTrack
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<GridTrack, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<GridTrack, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
Source§impl From<GridTrack> for RepeatedGridTrack
impl From<GridTrack> for RepeatedGridTrack
Source§fn from(track: GridTrack) -> RepeatedGridTrack
fn from(track: GridTrack) -> RepeatedGridTrack
Source§impl FromReflect for GridTrack
impl FromReflect for GridTrack
Source§fn from_reflect(reflect: &(dyn PartialReflect + 'static)) -> Option<GridTrack>
fn from_reflect(reflect: &(dyn PartialReflect + 'static)) -> Option<GridTrack>
Self from a reflected value.Source§fn take_from_reflect(
reflect: Box<dyn PartialReflect>,
) -> Result<Self, Box<dyn PartialReflect>>
fn take_from_reflect( reflect: Box<dyn PartialReflect>, ) -> Result<Self, Box<dyn PartialReflect>>
Self using,
constructing the value using from_reflect if that fails. Read moreSource§impl GetOwnership for GridTrack
impl GetOwnership for GridTrack
Source§impl GetTypeRegistration for GridTrack
impl GetTypeRegistration for GridTrack
Source§fn get_type_registration() -> TypeRegistration
fn get_type_registration() -> TypeRegistration
TypeRegistration for this type.Source§fn register_type_dependencies(registry: &mut TypeRegistry)
fn register_type_dependencies(registry: &mut TypeRegistry)
Source§impl IntoReturn for GridTrack
impl IntoReturn for GridTrack
Source§impl PartialReflect for GridTrack
impl PartialReflect for GridTrack
Source§fn get_represented_type_info(&self) -> Option<&'static TypeInfo>
fn get_represented_type_info(&self) -> Option<&'static TypeInfo>
Source§fn try_apply(
&mut self,
value: &(dyn PartialReflect + 'static),
) -> Result<(), ApplyError>
fn try_apply( &mut self, value: &(dyn PartialReflect + 'static), ) -> Result<(), ApplyError>
Source§fn reflect_kind(&self) -> ReflectKind
fn reflect_kind(&self) -> ReflectKind
Source§fn reflect_ref(&self) -> ReflectRef<'_>
fn reflect_ref(&self) -> ReflectRef<'_>
Source§fn reflect_mut(&mut self) -> ReflectMut<'_>
fn reflect_mut(&mut self) -> ReflectMut<'_>
Source§fn reflect_owned(self: Box<GridTrack>) -> ReflectOwned
fn reflect_owned(self: Box<GridTrack>) -> ReflectOwned
Source§fn try_into_reflect(
self: Box<GridTrack>,
) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>>
fn try_into_reflect( self: Box<GridTrack>, ) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>>
Source§fn try_as_reflect(&self) -> Option<&(dyn Reflect + 'static)>
fn try_as_reflect(&self) -> Option<&(dyn Reflect + 'static)>
Source§fn try_as_reflect_mut(&mut self) -> Option<&mut (dyn Reflect + 'static)>
fn try_as_reflect_mut(&mut self) -> Option<&mut (dyn Reflect + 'static)>
Source§fn into_partial_reflect(self: Box<GridTrack>) -> Box<dyn PartialReflect>
fn into_partial_reflect(self: Box<GridTrack>) -> Box<dyn PartialReflect>
Source§fn as_partial_reflect(&self) -> &(dyn PartialReflect + 'static)
fn as_partial_reflect(&self) -> &(dyn PartialReflect + 'static)
Source§fn as_partial_reflect_mut(&mut self) -> &mut (dyn PartialReflect + 'static)
fn as_partial_reflect_mut(&mut self) -> &mut (dyn PartialReflect + 'static)
Source§fn reflect_partial_eq(
&self,
value: &(dyn PartialReflect + 'static),
) -> Option<bool>
fn reflect_partial_eq( &self, value: &(dyn PartialReflect + 'static), ) -> Option<bool>
Source§fn reflect_partial_cmp(
&self,
value: &(dyn PartialReflect + 'static),
) -> Option<Ordering>
fn reflect_partial_cmp( &self, value: &(dyn PartialReflect + 'static), ) -> Option<Ordering>
Source§fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError>
fn reflect_clone(&self) -> Result<Box<dyn Reflect>, ReflectCloneError>
Self using reflection. Read moreSource§fn apply(&mut self, value: &(dyn PartialReflect + 'static))
fn apply(&mut self, value: &(dyn PartialReflect + 'static))
Source§fn to_dynamic(&self) -> Result<Box<dyn PartialReflect>, ReflectCloneError>
fn to_dynamic(&self) -> Result<Box<dyn PartialReflect>, ReflectCloneError>
Source§fn reflect_clone_and_take<T>(&self) -> Result<T, ReflectCloneError>
fn reflect_clone_and_take<T>(&self) -> Result<T, ReflectCloneError>
PartialReflect, combines reflect_clone and
take in a useful fashion, automatically constructing an appropriate
ReflectCloneError if the downcast fails.Source§fn reflect_hash(&self) -> Option<u64>
fn reflect_hash(&self) -> Option<u64>
Source§fn debug(&self, f: &mut Formatter<'_>) -> Result<(), Error>
fn debug(&self, f: &mut Formatter<'_>) -> Result<(), Error>
Source§fn is_dynamic(&self) -> bool
fn is_dynamic(&self) -> bool
Source§impl Reflect for GridTrack
impl Reflect for GridTrack
Source§fn into_any(self: Box<GridTrack>) -> Box<dyn Any>
fn into_any(self: Box<GridTrack>) -> Box<dyn Any>
Box<dyn Any>. Read moreSource§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut dyn Any. Read moreSource§fn into_reflect(self: Box<GridTrack>) -> Box<dyn Reflect>
fn into_reflect(self: Box<GridTrack>) -> Box<dyn Reflect>
Source§fn as_reflect(&self) -> &(dyn Reflect + 'static)
fn as_reflect(&self) -> &(dyn Reflect + 'static)
Source§fn as_reflect_mut(&mut self) -> &mut (dyn Reflect + 'static)
fn as_reflect_mut(&mut self) -> &mut (dyn Reflect + 'static)
Source§impl Serialize for GridTrack
impl Serialize for GridTrack
Source§fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
fn serialize<__S>(
&self,
__serializer: __S,
) -> Result<<__S as Serializer>::Ok, <__S as Serializer>::Error>where
__S: Serializer,
Source§impl Struct for GridTrack
impl Struct for GridTrack
Source§fn field(&self, name: &str) -> Option<&(dyn PartialReflect + 'static)>
fn field(&self, name: &str) -> Option<&(dyn PartialReflect + 'static)>
name as a &dyn PartialReflect.Source§fn field_mut(
&mut self,
name: &str,
) -> Option<&mut (dyn PartialReflect + 'static)>
fn field_mut( &mut self, name: &str, ) -> Option<&mut (dyn PartialReflect + 'static)>
name as a
&mut dyn PartialReflect.Source§fn field_at(&self, index: usize) -> Option<&(dyn PartialReflect + 'static)>
fn field_at(&self, index: usize) -> Option<&(dyn PartialReflect + 'static)>
index as a
&dyn PartialReflect.Source§fn field_at_mut(
&mut self,
index: usize,
) -> Option<&mut (dyn PartialReflect + 'static)>
fn field_at_mut( &mut self, index: usize, ) -> Option<&mut (dyn PartialReflect + 'static)>
index
as a &mut dyn PartialReflect.Source§fn index_of_name(&self, name: &str) -> Option<usize>
fn index_of_name(&self, name: &str) -> Option<usize>
Source§fn iter_fields(&self) -> FieldIter<'_> ⓘ
fn iter_fields(&self) -> FieldIter<'_> ⓘ
Source§fn to_dynamic_struct(&self) -> Result<DynamicStruct, ReflectCloneError>
fn to_dynamic_struct(&self) -> Result<DynamicStruct, ReflectCloneError>
DynamicStruct from this struct. Read moreSource§fn get_represented_struct_info(&self) -> Option<&'static StructInfo>
fn get_represented_struct_info(&self) -> Option<&'static StructInfo>
None if TypeInfo is not available.impl StructuralPartialEq for GridTrack
Source§impl TypePath for GridTrack
impl TypePath for GridTrack
Source§fn type_path() -> &'static str
fn type_path() -> &'static str
Source§fn short_type_path() -> &'static str
fn short_type_path() -> &'static str
Source§fn type_ident() -> Option<&'static str>
fn type_ident() -> Option<&'static str>
Source§fn crate_name() -> Option<&'static str>
fn crate_name() -> Option<&'static str>
Auto Trait Implementations§
impl Freeze for GridTrack
impl RefUnwindSafe for GridTrack
impl Send for GridTrack
impl Sync for GridTrack
impl Unpin for GridTrack
impl UnsafeUnpin for GridTrack
impl UnwindSafe for GridTrack
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T ShaderType for self. When used in AsBindGroup
derives, it is safe to assume that all images in self exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> Brush for T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ConditionalSend for Twhere
T: Send,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<T> DynamicTypePath for Twhere
T: TypePath,
impl<T> DynamicTypePath for Twhere
T: TypePath,
Source§fn reflect_type_path(&self) -> &str
fn reflect_type_path(&self) -> &str
TypePath::type_path.Source§fn reflect_short_type_path(&self) -> &str
fn reflect_short_type_path(&self) -> &str
Source§fn reflect_type_ident(&self) -> Option<&str>
fn reflect_type_ident(&self) -> Option<&str>
TypePath::type_ident.Source§fn reflect_crate_name(&self) -> Option<&str>
fn reflect_crate_name(&self) -> Option<&str>
TypePath::crate_name.Source§fn reflect_module_path(&self) -> Option<&str>
fn reflect_module_path(&self) -> Option<&str>
Source§impl<T> DynamicTyped for Twhere
T: Typed,
impl<T> DynamicTyped for Twhere
T: Typed,
Source§fn reflect_type_info(&self) -> &'static TypeInfo
fn reflect_type_info(&self) -> &'static TypeInfo
Typed::type_info.impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> FromTemplate for T
impl<T> FromTemplate for T
Source§impl<T> FromWorld for Twhere
T: Default,
impl<T> FromWorld for Twhere
T: Default,
Source§fn from_world(_world: &mut World) -> T
fn from_world(_world: &mut World) -> T
Creates Self using default().
Source§impl<S> GetField for Swhere
S: Struct,
impl<S> GetField for Swhere
S: Struct,
Source§impl<T> GetPath for T
impl<T> GetPath for T
Source§fn reflect_path<'p>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&(dyn PartialReflect + 'static), ReflectPathError<'p>>
fn reflect_path<'p>( &self, path: impl ReflectPath<'p>, ) -> Result<&(dyn PartialReflect + 'static), ReflectPathError<'p>>
path. Read moreSource§fn reflect_path_mut<'p>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut (dyn PartialReflect + 'static), ReflectPathError<'p>>
fn reflect_path_mut<'p>( &mut self, path: impl ReflectPath<'p>, ) -> Result<&mut (dyn PartialReflect + 'static), ReflectPathError<'p>>
path. Read moreSource§fn path<'p, T>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&T, ReflectPathError<'p>>where
T: Reflect,
fn path<'p, T>(
&self,
path: impl ReflectPath<'p>,
) -> Result<&T, ReflectPathError<'p>>where
T: Reflect,
path. Read moreSource§fn path_mut<'p, T>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut T, ReflectPathError<'p>>where
T: Reflect,
fn path_mut<'p, T>(
&mut self,
path: impl ReflectPath<'p>,
) -> Result<&mut T, ReflectPathError<'p>>where
T: Reflect,
path. Read moreSource§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T> HitDataExtra for T
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> InitializeFromFunction<T> for T
impl<T> InitializeFromFunction<T> for T
Source§fn initialize_from_function(f: fn() -> T) -> T
fn initialize_from_function(f: fn() -> T) -> T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
Source§impl<G> PatchFromTemplate for Gwhere
G: FromTemplate,
impl<G> PatchFromTemplate for Gwhere
G: FromTemplate,
Source§fn patch<F>(func: F) -> TemplatePatch<F, <G as PatchFromTemplate>::Template>
fn patch<F>(func: F) -> TemplatePatch<F, <G as PatchFromTemplate>::Template>
func, and turns it into a TemplatePatch.Source§impl<T> PatchTemplate for Twhere
T: Template,
impl<T> PatchTemplate for Twhere
T: Template,
Source§fn patch_template<F>(func: F) -> TemplatePatch<F, T>
fn patch_template<F>(func: F) -> TemplatePatch<F, T>
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
ReadEndian::read_from_little_endian().impl<T> Reflectable for T
Source§impl<T> Serialize for T
impl<T> Serialize for T
fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>
fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>
impl<T> Settings for T
Source§impl<Ret> SpawnIfAsync<(), Ret> for Ret
impl<Ret> SpawnIfAsync<(), Ret> for Ret
Source§impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
Source§fn super_from(input: T) -> O
fn super_from(input: T) -> O
Source§impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
Source§fn super_into(self) -> O
fn super_into(self) -> O
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.Source§impl<T> Template for T
impl<T> Template for T
Source§fn build_template(
&self,
_context: &mut TemplateContext<'_, '_>,
) -> Result<<T as Template>::Output, BevyError>
fn build_template( &self, _context: &mut TemplateContext<'_, '_>, ) -> Result<<T as Template>::Output, BevyError>
entity context to produce a Template::Output.Source§fn clone_template(&self) -> T
fn clone_template(&self) -> T
Clone.Source§impl<T, U> ToSample<U> for Twhere
U: FromSample<T>,
impl<T, U> ToSample<U> for Twhere
U: FromSample<T>,
fn to_sample_(self) -> U
Source§impl<T> TypeData for T
impl<T> TypeData for T
Source§fn clone_type_data(&self) -> Box<dyn TypeData>
fn clone_type_data(&self) -> Box<dyn TypeData>
self.