diff options
-rw-r--r-- | lang/english.txt | 8 | ||||
-rw-r--r-- | macros.h | 1 | ||||
-rw-r--r-- | misc_gui.c | 72 | ||||
-rw-r--r-- | settings.c | 1 | ||||
-rw-r--r-- | settings_gui.c | 1 | ||||
-rw-r--r-- | variables.h | 1 | ||||
-rw-r--r-- | viewport.c | 232 | ||||
-rw-r--r-- | window.h | 8 |
8 files changed, 279 insertions, 45 deletions
diff --git a/lang/english.txt b/lang/english.txt index 0de6b0ed2..35d197f62 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -1065,6 +1065,7 @@ STR_CONFIG_PATCHES_STATION_SPREAD :{LTBLUE}Max sta STR_CONFIG_PATCHES_SERVICEATHELIPAD :{LTBLUE}Service helicopters at helipads automatically: {ORANGE}{STRING1} STR_CONFIG_PATCHES_LINK_TERRAFORM_TOOLBAR :{LTBLUE}Link landscape toolbar to rail/road/water/airport toolbars: {ORANGE}{STRING1} STR_CONFIG_PATCHES_REVERSE_SCROLLING :{LTBLUE}Reverse scroll direction: {ORANGE}{STRING1} +STR_CONFIG_PATCHES_MEASURE_TOOLTIP :{LTBLUE}Show a measurement tooltip when using various build-tools: {ORANGE}{STRING1} STR_CONFIG_PATCHES_LIVERIES :{LTBLUE}Show company liveries: {ORANGE}{STRING1} STR_CONFIG_PATCHES_LIVERIES_NONE :None STR_CONFIG_PATCHES_LIVERIES_OWN :Own company @@ -3093,4 +3094,11 @@ STR_LARGE_AIRPORTS :{BLACK}Large ai STR_HUB_AIRPORTS :{BLACK}Hub airports STR_HELIPORTS :{BLACK}Helicopter airports +############ Tooltip measurment + +STR_MEASURE_LENGTH :{BLACK}Length: {NUM} +STR_MEASURE_AREA :{BLACK}Area: {NUM} x {NUM} +STR_MEASURE_LENGTH_HEIGHTDIFF :{BLACK}Length: {NUM}{}Height difference: {NUM} m +STR_MEASURE_AREA_HEIGHTDIFF :{BLACK}Area: {NUM} x {NUM}{}Height difference: {NUM} m + ######## @@ -148,6 +148,7 @@ static inline int64 myabs64(int64 a) { if (a<0) a = -a; return a; } static inline void swap_byte(byte *a, byte *b) { byte t = *a; *a = *b; *b = t; } static inline void swap_uint16(uint16 *a, uint16 *b) { uint16 t = *a; *a = *b; *b = t; } static inline void swap_int16(int16 *a, int16 *b) { int16 t = *a; *a = *b; *b = t; } +static inline void swap_uint32(uint32 *a, uint32 *b) { uint32 t = *a; *a = *b; *b = t; } static inline void swap_int32(int32 *a, int32 *b) { int32 t = *a; *a = *b; *b = t; } static inline void swap_tile(TileIndex *a, TileIndex *b) { TileIndex t = *a; *a = *b; *b = t; } diff --git a/misc_gui.c b/misc_gui.c index 047866eab..a812d5210 100644 --- a/misc_gui.c +++ b/misc_gui.c @@ -636,57 +636,77 @@ static const Widget _tooltips_widgets[] = { static void TooltipsWndProc(Window *w, WindowEvent *e) { switch (e->event) { - case WE_PAINT: + case WE_PAINT: { + uint arg; GfxFillRect(0, 0, w->width - 1, w->height - 1, 0); GfxFillRect(1, 1, w->width - 2, w->height - 2, 0x44); - DrawStringMultiCenter((w->width >> 1), (w->height >> 1) - 5, WP(w,tooltips_d).string_id, 197); + + for (arg = 0; arg < WP(w, tooltips_d).paramcount; arg++) { + SetDParam(arg, WP(w, tooltips_d).params[arg]); + } + DrawStringMultiCenter((w->width >> 1), (w->height >> 1) - 5, WP(w, tooltips_d).string_id, 197); break; + } case WE_MOUSELOOP: - if (!_right_button_down) DeleteWindow(w); + /* We can show tooltips while dragging tools. These are shown as long as + * we are dragging the tool. Normal tooltips work with rmb */ + if (WP(w, tooltips_d).paramcount == 0 ) { + if (!_right_button_down) DeleteWindow(w); + } else { + if (!_left_button_down) DeleteWindow(w); + } + break; } } -void GuiShowTooltips(StringID string_id) +/** Shows a tooltip +* @param str String to be displayed +* @param params (optional) up to 5 pieces of additional information that may be +* added to a tooltip; currently only supports parameters of {NUM} (integer) */ +void GuiShowTooltipsWithArgs(StringID str, uint paramcount, uint32 params[]) { char buffer[512]; - Window *w; - int right,bottom; - int x,y; + BoundingRect br; + uint i; + int x, y; - if (string_id == 0) return; + Window *w = FindWindowById(WC_TOOLTIPS, 0); + if (w != NULL) DeleteWindow(w); - w = FindWindowById(WC_TOOLTIPS, 0); - if (w != NULL) { - if (WP(w,tooltips_d).string_id == string_id) - return; - DeleteWindow(w); - } + /* We only show measurement tooltips with shift pressed down */ + if (paramcount != 0 && !_patches.measure_tooltip) return; - GetString(buffer, string_id); + for (i = 0; i != paramcount; i++) SetDParam(i, params[i]); + GetString(buffer, str); - right = GetStringBoundingBox(buffer).width + 6; + br = GetStringBoundingBox(buffer); + br.width += 6; br.height += 4; // increase slightly to have some space around the box /* Cut tooltip length to 200 pixels max, wrap to new line if longer */ - bottom = 14; - if (right > 200) { - bottom += ((right - 4) / 176) * 10; - right = 200; + if (br.width > 200) { + br.height += ((br.width - 4) / 176) * 10; + br.width = 200; } /* Correctly position the tooltip position, watch out for window and cursor size * Clamp value to below main toolbar and above statusbar. If tooltip would * go below window, flip it so it is shown above the cursor */ y = clamp(_cursor.pos.y + _cursor.size.y + _cursor.offs.y + 5, 22, _screen.height - 12); - if (y + bottom > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - bottom - 5; - x = clamp(_cursor.pos.x - (right >> 1), 0, _screen.width - right); + if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5; + x = clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width); + + w = AllocateWindow(x, y, br.width, br.height, TooltipsWndProc, WC_TOOLTIPS, _tooltips_widgets); + + WP(w, tooltips_d).string_id = str; + assert(sizeof(WP(w, tooltips_d).params[0]) == sizeof(params[0])); + memcpy(WP(w, tooltips_d).params, params, sizeof(WP(w, tooltips_d).params[0]) * paramcount); + WP(w, tooltips_d).paramcount = paramcount; - w = AllocateWindow(x, y, right, bottom, TooltipsWndProc, WC_TOOLTIPS, _tooltips_widgets); - WP(w,tooltips_d).string_id = string_id; w->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip - w->widget[0].right = right; - w->widget[0].bottom = bottom; + w->widget[0].right = br.width; + w->widget[0].bottom = br.height; } diff --git a/settings.c b/settings.c index b8b72dd54..06a4870ba 100644 --- a/settings.c +++ b/settings.c @@ -1254,6 +1254,7 @@ const SettingDesc _patch_settings[] = { SDT_BOOL(Patches, show_finances, S, 0, true, STR_CONFIG_PATCHES_SHOWFINANCES, NULL), SDT_BOOL(Patches, autoscroll, S, 0, false, STR_CONFIG_PATCHES_AUTOSCROLL, NULL), SDT_BOOL(Patches, reverse_scroll, S, 0, false, STR_CONFIG_PATCHES_REVERSE_SCROLLING, NULL), + SDT_BOOL(Patches, measure_tooltip, S, 0, false, STR_CONFIG_PATCHES_MEASURE_TOOLTIP, NULL), SDT_VAR(Patches, errmsg_duration, SLE_UINT8, S, 0, 5, 0, 20, 0, STR_CONFIG_PATCHES_ERRMSG_DURATION, NULL), SDT_VAR(Patches, toolbar_pos, SLE_UINT8, S,MS, 0, 0, 2, 0, STR_CONFIG_PATCHES_TOOLBAR_POS, v_PositionMainToolbar), SDT_VAR(Patches, window_snap_radius, SLE_UINT8, S,D0, 10, 1, 32, 0, STR_CONFIG_PATCHES_SNAP_RADIUS, NULL), diff --git a/settings_gui.c b/settings_gui.c index 6530aa137..b93a480c8 100644 --- a/settings_gui.c +++ b/settings_gui.c @@ -566,6 +566,7 @@ static const char *_patches_ui[] = { "reverse_scroll", "errmsg_duration", "toolbar_pos", + "measure_tooltip", "window_snap_radius", "invisible_trees", "population_in_label", diff --git a/variables.h b/variables.h index b7a1071cd..ef3d04b42 100644 --- a/variables.h +++ b/variables.h @@ -126,6 +126,7 @@ typedef struct Patches { bool no_servicing_if_no_breakdowns; // dont send vehicles to depot when breakdowns are disabled bool link_terraform_toolbar; // display terraform toolbar when displaying rail, road, water and airport toolbars bool reverse_scroll; // Right-Click-Scrolling scrolls in the opposite direction + bool measure_tooltip; // Show a permanent tooltip when dragging tools byte liveries; // Options for displaying company liveries, 0=none, 1=self, 2=all uint8 toolbar_pos; // position of toolbars, 0=left, 1=center, 2=right diff --git a/viewport.c b/viewport.c index 5db949401..1053c0c21 100644 --- a/viewport.c +++ b/viewport.c @@ -1937,13 +1937,22 @@ void VpSetPlaceSizingLimit(int limit) _thd.sizelimit = limit; } -void VpSetPresizeRange(uint from, uint to) +/** +* Highlights all tiles between a set of two tiles. Used in dock and tunnel placement +* @param from TileIndex of the first tile to highlight +* @param to TileIndex of the last tile to highlight */ +void VpSetPresizeRange(TileIndex from, TileIndex to) { + uint distance = DistanceManhattan(from, to) + 1; + _thd.selend.x = TileX(to) * TILE_SIZE; _thd.selend.y = TileY(to) * TILE_SIZE; _thd.selstart.x = TileX(from) * TILE_SIZE; _thd.selstart.y = TileY(from) * TILE_SIZE; _thd.next_drawstyle = HT_RECT; + + /* show measurement only if there is any length to speak of */ + if (distance > 1) GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH, 1, &distance); } static void VpStartPreSizing(void) @@ -1986,6 +1995,120 @@ static byte Check2x1AutoRail(int mode) return 0; // avoids compiler warnings } +/** Check if the direction of start and end tile should be swapped based on + * the dragging-style. Default directions are: + * in the case of a line (HT_RAIL, HT_LINE): DIR_NE, DIR_NW, DIR_N, DIR_E + * in the case of a rect (HT_RECT, HT_POINT): DIR_S, DIR_E + * For example dragging a rectangle area from south to north should be swapped to + * north-south (DIR_S) to obtain the same results with less code. This is what + * the return value signifies. + * @param style HighLightStyle dragging style + * @param start_tile, end_tile start and end tile of drag + * @param boolean value which when true means start/end should be swapped */ +static bool SwapDirection(HighLightStyle style, TileIndex start_tile, TileIndex end_tile) +{ + uint start_x = TileX(start_tile); + uint start_y = TileY(start_tile); + uint end_x = TileX(end_tile); + uint end_y = TileY(end_tile); + + switch (style & HT_DRAG_MASK) { + case HT_RAIL: + case HT_LINE: return (end_x > start_x || (end_x == start_x && end_y > start_y)); + + case HT_RECT: + case HT_POINT: return (end_x != start_x && end_y < start_y); + default: NOT_REACHED(); + } + + return false; +} + +/** Calculates height difference between one tile and another +* Multiplies the result to suit the standard given by minimap - 50 meters high +* To correctly get the height difference we need the direction we are dragging +* in, as well as with what kind of tool we are dragging. For example a horizontal +* autorail tool that starts in bottom and ends at the top of a tile will need the +* maximum of SW,S and SE,N corners respectively. This is handled by the lookup table below +* See _tileoffs_by_dir in map.c for the direction enums if you can't figure out +* the values yourself. +* @param style HightlightStyle of drag. This includes direction and style (autorail, rect, etc.) +* @param distance amount of tiles dragged, important for horizontal/vertical drags +* ignored for others +* @param start_tile, end_tile start and end tile of drag operation +* @return height difference between two tiles. Tile measurement tool utilizes +* this value in its tooltips */ +static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_tile, TileIndex end_tile) +{ + bool swap = SwapDirection(style, start_tile, end_tile); + byte style_t; + uint h0, h1, ht; // start heigth, end height, and temp variable + + if (start_tile == end_tile) return 0; + if (swap) swap_tile(&start_tile, &end_tile); + + switch (style & HT_DRAG_MASK) { + case HT_RECT: { + static const TileIndexDiffC heightdiff_area_by_dir[] = { + /* Start */ {1, 0}, /* Dragging east */ {0, 0}, /* Dragging south */ + /* End */ {0, 1}, /* Dragging east */ {1, 1} /* Dragging south */ + }; + + /* In the case of an area we can determine whether we were dragging south or + * east by checking the X-coordinates of the tiles */ + style_t = (byte)(TileX(end_tile) > TileX(start_tile)); + start_tile = TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_area_by_dir[style_t])); + end_tile = TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_area_by_dir[2 + style_t])); + } + /* Fallthrough */ + case HT_POINT: + h0 = TileHeight(start_tile); + h1 = TileHeight(end_tile); + break; + default: { /* All other types, this is mostly only line/autorail */ + static const HighLightStyle flip_style_direction[] = { + HT_DIR_X, HT_DIR_Y, HT_DIR_HL, HT_DIR_HU, HT_DIR_VR, HT_DIR_VL + }; + static const TileIndexDiffC heightdiff_line_by_dir[] = { + /* Start */ {1, 0}, {1, 1}, /* HT_DIR_X */ {0, 1}, {1, 1}, /* HT_DIR_Y */ + /* Start */ {1, 0}, {0, 0}, /* HT_DIR_HU */ {1, 0}, {1, 1}, /* HT_DIR_HL */ + /* Start */ {1, 0}, {1, 1}, /* HT_DIR_VL */ {0, 1}, {1, 1}, /* HT_DIR_VR */ + + /* Start */ {0, 1}, {0, 0}, /* HT_DIR_X */ {1, 0}, {0, 0}, /* HT_DIR_Y */ + /* End */ {0, 1}, {0, 0}, /* HT_DIR_HU */ {1, 1}, {0, 1}, /* HT_DIR_HL */ + /* End */ {1, 0}, {0, 0}, /* HT_DIR_VL */ {0, 0}, {0, 1}, /* HT_DIR_VR */ + }; + + distance %= 2; // we're only interested if the distance is even or uneven + style &= HT_DIR_MASK; + + /* To handle autorail, we do some magic to be able to use a lookup table. + * Firstly if we drag the other way around, we switch start&end, and if needed + * also flip the drag-position. Eg if it was on the left, and the distance is even + * that means the end, which is now the start is on the right */ + if (swap && distance == 0) style = flip_style_direction[style]; + + /* Use lookup table for start-tile based on HighLightStyle direction */ + style_t = style * 2; + assert(style_t < lengthof(heightdiff_line_by_dir) - 13); + h0 = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t]))); + ht = TileHeight(TILE_ADD(start_tile, ToTileIndexDiff(heightdiff_line_by_dir[style_t + 1]))); + h0 = maxu(h0, ht); + + /* Use lookup table for end-tile based on HighLightStyle direction + * flip around side (lower/upper, left/right) based on distance */ + if (distance == 0) style_t = flip_style_direction[style] * 2; + assert(style_t < lengthof(heightdiff_line_by_dir) - 13); + h1 = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t]))); + ht = TileHeight(TILE_ADD(end_tile, ToTileIndexDiff(heightdiff_line_by_dir[12 + style_t + 1]))); + h1 = maxu(h1, ht); + } break; + } + + if (swap) swap_uint32(&h0, &h1); + /* Minimap shows height in intervals of 50 meters, let's do the same */ + return (int)(h1 - h0) * 50; +} // while dragging static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int method) @@ -2087,6 +2210,30 @@ static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int meth } } } + + if (_patches.measure_tooltip) { + TileIndex t0 = TileVirtXY(thd->selstart.x, thd->selstart.y); + TileIndex t1 = TileVirtXY(x, y); + uint distance = DistanceManhattan(t0, t1) + 1; + int heightdiff = CalcHeightdiff(b, distance, t0, t1); + uint params[2]; + + /* If we are showing a tooltip for horizontal or vertical drags, + * 2 tiles have a length of 1. To bias towards the ceiling we add + * one before division. It feels more natural to count 3 lengths as 2 */ + if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) { + distance = (distance + 1) / 2; + } + + params[0] = distance; + if (heightdiff == 0) { + GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH, 1, params); + } else { + params[1] = heightdiff; + GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH_HEIGHTDIFF, 2, params); + } + } + thd->selend.x = x; thd->selend.y = y; thd->next_drawstyle = b; @@ -2101,6 +2248,7 @@ static void CalcRaildirsDrawstyle(TileHighlightData *thd, int x, int y, int meth void VpSelectTilesWithMethod(int x, int y, int method) { int sx, sy; + HighLightStyle style; if (x == -1) { _thd.selend.x = -1; @@ -2124,32 +2272,80 @@ void VpSelectTilesWithMethod(int x, int y, int method) sy = _thd.selstart.y; switch (method) { - case VPM_FIX_X: - x = sx; - break; - - case VPM_FIX_Y: - y = sy; - break; - - case VPM_X_OR_Y: + case VPM_X_OR_Y: /* drag in X or Y direction */ if (myabs(sy - y) < myabs(sx - x)) { y = sy; + style = HT_DIR_X; } else { x = sx; + style = HT_DIR_Y; } - break; - - case VPM_X_AND_Y: - break; + goto calc_heightdiff_single_direction; + case VPM_FIX_X: /* drag in Y direction */ + x = sx; + style = HT_DIR_Y; + goto calc_heightdiff_single_direction; + case VPM_FIX_Y: /* drag in X direction */ + y = sy; + style = HT_DIR_X; + +calc_heightdiff_single_direction:; + if (_patches.measure_tooltip) { + TileIndex t0 = TileVirtXY(sx, sy); + TileIndex t1 = TileVirtXY(x, y); + uint distance = DistanceManhattan(t0, t1) + 1; + int heightdiff = CalcHeightdiff((_thd.next_drawstyle & HT_DRAG_MASK) | style, 0, t0, t1); + uint params[2]; + + params[0] = distance; + if (heightdiff == 0) { + GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH, 1, params); + } else { + params[1] = heightdiff; + GuiShowTooltipsWithArgs(STR_MEASURE_LENGTH_HEIGHTDIFF, 2, params); + } + } break; - // limit the selected area to a 10x10 rect. - case VPM_X_AND_Y_LIMITED: { - int limit = (_thd.sizelimit - 1) * 16; + case VPM_X_AND_Y_LIMITED: { /* drag an X by Y constrained rect area */ + int limit = (_thd.sizelimit - 1) * TILE_SIZE; x = sx + clamp(x - sx, -limit, limit); y = sy + clamp(y - sy, -limit, limit); - break; + /* Fallthrough */ + case VPM_X_AND_Y: { /* drag an X by Y area */ + if (_patches.measure_tooltip) { + TileIndex t0 = TileVirtXY(sx, sy); + TileIndex t1 = TileVirtXY(x, y); + uint dx = abs(TileX(t0) - TileX(t1)) + 1; + uint dy = abs(TileY(t0) - TileY(t1)) + 1; + HighLightStyle style = _thd.next_drawstyle; + int heightdiff; + uint params[3]; + + /* If dragging an area (eg dynamite tool) and it is actually a single + * row/column, change the type to 'line' to get proper calculation for height */ + if (style & HT_RECT) { + if (dx == 1) { + style = HT_LINE | HT_DIR_Y; + } else if (dy == 1) { + style = HT_LINE | HT_DIR_X; + } + } + + heightdiff = CalcHeightdiff(style, 0, t0, t1); + + params[0] = dx; + params[1] = dy; + if (heightdiff == 0) { + GuiShowTooltipsWithArgs(STR_MEASURE_AREA, 2, params); + } else { + params[2] = heightdiff; + GuiShowTooltipsWithArgs(STR_MEASURE_AREA_HEIGHTDIFF, 3, params); + } + } + } break; + } + default: NOT_REACHED(); } _thd.selend.x = x; @@ -387,6 +387,8 @@ assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(tree_d)); typedef struct { StringID string_id; + byte paramcount; + uint32 params[5]; } tooltips_d; assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(tooltips_d)); @@ -805,7 +807,11 @@ void CDECL SetWindowWidgetsHiddenState(Window *w, bool hidden_stat, int widgets, void CDECL SetWindowWidgetsLoweredState(Window *w, bool lowered_stat, int widgets, ...); /* misc_gui.c*/ -void GuiShowTooltips(StringID string_id); +void GuiShowTooltipsWithArgs(StringID str, uint paramcount, uint params[]); +static inline void GuiShowTooltips(StringID str) +{ + GuiShowTooltipsWithArgs(str, 0, NULL); +} /* widget.c */ int GetWidgetFromPos(const Window *w, int x, int y); |