1 module rpui.widget_locator; 2 3 import std.math; 4 5 import rpui.primitives; 6 import gapi.vec; 7 8 import rpui.widget; 9 10 package final class WidgetLocator { 11 private Widget holder; 12 13 this(Widget widget) { 14 this.holder = widget; 15 } 16 17 void updateAbsolutePosition() { 18 with (holder) { 19 vec2 res = vec2(0, 0); 20 Widget lastParent = parent; 21 22 while (lastParent !is null) { 23 res += lastParent.position - lastParent.contentOffset; 24 res += lastParent.innerOffsetStart + lastParent.outerOffsetStart; 25 lastParent = lastParent.parent; 26 } 27 28 absolutePosition_ = position + res + outerOffsetStart; 29 absolutePosition_.x = round(absolutePosition_.x); 30 absolutePosition_.y = round(absolutePosition_.y); 31 } 32 } 33 34 void updateLocationAlign() { 35 with (holder) { 36 switch (locationAlign) { 37 case Align.left: 38 position.x = 0; 39 break; 40 41 case Align.right: 42 position.x = parent.innerBoundarySizeClamped.x - size.x - outerOffsetSize.x; 43 break; 44 45 case Align.center: 46 const halfSize = (parent.innerBoundarySizeClamped.x - size.x - outerOffsetSize.x) / 2; 47 position.x = round(halfSize); 48 break; 49 50 default: 51 break; 52 } 53 } 54 } 55 56 void updateVerticalLocationAlign() { 57 with (holder) { 58 switch (verticalLocationAlign) { 59 case VerticalAlign.top: 60 position.y = 0; 61 break; 62 63 case VerticalAlign.bottom: 64 position.y = parent.innerBoundarySizeClamped.y - size.y - outerOffsetSize.y; 65 break; 66 67 case VerticalAlign.middle: 68 const halfSize = (parent.innerBoundarySizeClamped.y - size.y - outerOffsetSize.y) / 2; 69 position.y = round(halfSize); 70 break; 71 72 default: 73 break; 74 } 75 } 76 } 77 78 void updateRegionAlign() { 79 with (holder) { 80 if (regionAlign == RegionAlign.none) 81 return; 82 83 const FrameRect region = locator.findRegion(); 84 const vec2 regionSize = vec2( 85 parent.innerSize.x - region.right - region.left - outerOffsetSize.x, 86 parent.innerSize.y - region.bottom - region.top - outerOffsetSize.y 87 ); 88 89 const vec2 fullRegionSize = vec2( 90 parent.size.x - region.right - region.left - outerOffsetSize.x - parent.innerOffsetSize.x, 91 parent.size.y - region.bottom - region.top - outerOffsetSize.y - parent.innerOffsetSize.y 92 ); 93 94 outerBoundarySize = size; 95 96 switch (regionAlign) { 97 case RegionAlign.client: 98 size = regionSize; 99 outerBoundarySize = fullRegionSize; 100 position = vec2(region.left, region.top); 101 break; 102 103 case RegionAlign.top: 104 size.x = regionSize.x; 105 outerBoundarySize.x = fullRegionSize.x; 106 position = vec2(region.left, region.top); 107 break; 108 109 case RegionAlign.bottom: 110 size.x = regionSize.x; 111 outerBoundarySize.x = fullRegionSize.x; 112 position.x = region.left; 113 position.y = parent.innerSize.y - outerSize.y - region.bottom; 114 break; 115 116 case RegionAlign.left: 117 size.y = regionSize.y; 118 outerBoundarySize.y = fullRegionSize.y; 119 position = vec2(region.left, region.top); 120 break; 121 122 case RegionAlign.right: 123 size.y = regionSize.y; 124 outerBoundarySize.y = fullRegionSize.y; 125 position.x = parent.innerSize.x - outerSize.x - region.right; 126 position.y = region.top; 127 break; 128 129 default: 130 break; 131 } 132 } 133 } 134 135 FrameRect findRegion() { 136 FrameRect region; 137 138 foreach (Widget widget; holder.parent.children) { 139 if (widget == holder) 140 break; 141 142 if (!widget.isVisible || widget.regionAlign == RegionAlign.none) 143 continue; 144 145 switch (widget.regionAlign) { 146 case RegionAlign.top: 147 region.top += widget.size.y + widget.outerOffset.bottom; 148 break; 149 150 case RegionAlign.left: 151 region.left += widget.size.x + widget.outerOffset.right; 152 break; 153 154 case RegionAlign.bottom: 155 region.bottom += widget.size.y + widget.outerOffset.top; 156 break; 157 158 case RegionAlign.right: 159 region.right += widget.size.x + widget.outerOffset.left; 160 break; 161 162 default: 163 continue; 164 } 165 } 166 167 return region; 168 } 169 }