mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-10 08:00:05 +00:00
Feature: Variable GUI scale.
GUI scale is now variable from 100% to 500%, and no longer restricted to powers-of-2.
This commit is contained in:
parent
13d271217f
commit
9666e46739
@ -65,18 +65,19 @@ void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels)
|
||||
{
|
||||
if (pixels == 0) {
|
||||
/* Try to determine a good height based on the minimal height recommended by the font. */
|
||||
int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
|
||||
int scaled_height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
pixels = scaled_height;
|
||||
|
||||
TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head);
|
||||
if (head != nullptr) {
|
||||
/* Font height is minimum height plus the difference between the default
|
||||
* height for this font size and the small size. */
|
||||
int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
pixels = Clamp(std::min<uint>(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE);
|
||||
int diff = scaled_height - ScaleGUITrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
/* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
|
||||
pixels = std::min(std::max(std::min<int>(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
|
||||
}
|
||||
} else {
|
||||
pixels = ScaleFontTrad(pixels);
|
||||
pixels = ScaleGUITrad(pixels);
|
||||
}
|
||||
this->used_size = pixels;
|
||||
|
||||
|
@ -28,7 +28,8 @@ static const int ASCII_LETTERSTART = 32; ///< First printable ASCII letter.
|
||||
SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs), glyph_to_spriteid_map(nullptr)
|
||||
{
|
||||
this->InitializeUnicodeGlyphMap();
|
||||
this->height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
|
||||
this->height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
this->ascender = (this->height - ScaleSpriteTrad(this->GetDefaultFontHeight(this->fs))) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,7 +105,8 @@ void SpriteFontCache::ClearGlyphToSpriteMap()
|
||||
void SpriteFontCache::ClearFontCache()
|
||||
{
|
||||
Layouter::ResetFontCache(this->fs);
|
||||
this->height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
|
||||
this->height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
this->ascender = (this->height - ScaleSpriteTrad(this->GetDefaultFontHeight(this->fs))) / 2;
|
||||
}
|
||||
|
||||
const Sprite *SpriteFontCache::GetGlyph(GlyphID key)
|
||||
|
59
src/gfx.cpp
59
src/gfx.cpp
@ -61,12 +61,9 @@ static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode,
|
||||
|
||||
static ReusableBuffer<uint8> _cursor_backup;
|
||||
|
||||
ZoomLevel _gui_zoom; ///< GUI Zoom level
|
||||
ZoomLevel _font_zoom; ///< Font Zoom level
|
||||
|
||||
int8 _gui_zoom_cfg; ///< GUI zoom level in config.
|
||||
int8 _font_zoom_cfg; ///< Font zoom level in config.
|
||||
|
||||
ZoomLevel _gui_zoom = ZOOM_LVL_OUT_4X; ///< GUI Zoom level
|
||||
int _gui_scale = MIN_INTERFACE_SCALE; ///< GUI scale, 100 is 100%.
|
||||
int _gui_scale_cfg; ///< GUI scale in config.
|
||||
|
||||
/**
|
||||
* The rect for repaint.
|
||||
@ -2028,48 +2025,52 @@ void SortResolutions()
|
||||
void UpdateGUIZoom()
|
||||
{
|
||||
/* Determine real GUI zoom to use. */
|
||||
if (_gui_zoom_cfg == ZOOM_LVL_CFG_AUTO) {
|
||||
_gui_zoom = static_cast<ZoomLevel>(Clamp(VideoDriver::GetInstance()->GetSuggestedUIZoom(), _settings_client.gui.zoom_min, _settings_client.gui.zoom_max));
|
||||
if (_gui_scale_cfg == -1) {
|
||||
_gui_scale = VideoDriver::GetInstance()->GetSuggestedUIScale();
|
||||
} else {
|
||||
/* Ensure the gui_zoom is clamped between min/max. Change the
|
||||
* _gui_zoom_cfg if it isn't, as this is used to visually show the
|
||||
* selection in the Game Options. */
|
||||
_gui_zoom_cfg = Clamp(_gui_zoom_cfg, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
|
||||
_gui_zoom = static_cast<ZoomLevel>(_gui_zoom_cfg);
|
||||
_gui_scale = Clamp(_gui_scale_cfg, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
|
||||
}
|
||||
|
||||
/* Determine real font zoom to use. */
|
||||
if (_font_zoom_cfg == ZOOM_LVL_CFG_AUTO) {
|
||||
_font_zoom = static_cast<ZoomLevel>(VideoDriver::GetInstance()->GetSuggestedUIZoom());
|
||||
} else {
|
||||
_font_zoom = static_cast<ZoomLevel>(_font_zoom_cfg);
|
||||
}
|
||||
int8 new_zoom = ScaleGUITrad(1) <= 1 ? ZOOM_LVL_OUT_4X : ScaleGUITrad(1) >= 4 ? ZOOM_LVL_MIN : ZOOM_LVL_OUT_2X;
|
||||
/* Ensure the gui_zoom is clamped between min/max. */
|
||||
new_zoom = Clamp(new_zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
|
||||
_gui_zoom = static_cast<ZoomLevel>(new_zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve GUI zoom level and adjust GUI to new zoom, if auto-suggestion is requested.
|
||||
* @param automatic Set if the change is occuring due to OS DPI scaling being changed.
|
||||
* @returns true when the zoom level has changed, caller must call ReInitAllWindows(true)
|
||||
* after resizing the application's window/buffer.
|
||||
*/
|
||||
bool AdjustGUIZoom()
|
||||
bool AdjustGUIZoom(bool automatic)
|
||||
{
|
||||
auto old_zoom = _gui_zoom;
|
||||
ZoomLevel old_zoom = _gui_zoom;
|
||||
int old_scale = _gui_scale;
|
||||
UpdateGUIZoom();
|
||||
if (old_zoom == _gui_zoom) return false;
|
||||
GfxClearSpriteCache();
|
||||
VideoDriver::GetInstance()->ClearSystemSprites();
|
||||
if (old_scale == _gui_scale) return false;
|
||||
|
||||
/* Reload sprites if sprite zoom level has changed. */
|
||||
if (old_zoom != _gui_zoom) {
|
||||
GfxClearSpriteCache();
|
||||
VideoDriver::GetInstance()->ClearSystemSprites();
|
||||
UpdateCursorSize();
|
||||
}
|
||||
|
||||
ClearFontCache();
|
||||
GfxClearSpriteCache();
|
||||
LoadStringWidthTable();
|
||||
UpdateAllVirtCoords();
|
||||
|
||||
/* Adjust all window sizes to match the new zoom level, so that they don't appear
|
||||
to move around when the application is moved to a screen with different DPI. */
|
||||
auto zoom_shift = old_zoom - _gui_zoom;
|
||||
for (Window *w : Window::Iterate()) {
|
||||
w->left = AdjustByZoom(w->left, zoom_shift);
|
||||
w->top = AdjustByZoom(w->top, zoom_shift);
|
||||
w->width = AdjustByZoom(w->width, zoom_shift);
|
||||
w->height = AdjustByZoom(w->height, zoom_shift);
|
||||
if (automatic) {
|
||||
w->left = (w->left * _gui_scale) / old_scale;
|
||||
w->top = (w->top * _gui_scale) / old_scale;
|
||||
w->width = (w->width * _gui_scale) / old_scale;
|
||||
w->height = (w->height * _gui_scale) / old_scale;
|
||||
}
|
||||
if (w->viewport != nullptr) {
|
||||
w->viewport->zoom = Clamp(ZoomLevel(w->viewport->zoom - zoom_shift), _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
|
||||
}
|
||||
|
@ -79,8 +79,7 @@ void ChangeGameSpeed(bool enable_fast_forward);
|
||||
void DrawMouseCursor();
|
||||
void ScreenSizeChanged();
|
||||
void GameSizeChanged();
|
||||
void UpdateGUIZoom();
|
||||
bool AdjustGUIZoom();
|
||||
bool AdjustGUIZoom(bool automatic);
|
||||
void UndrawMouseCursor();
|
||||
|
||||
/** Size of the buffer used for drawing strings. */
|
||||
|
@ -339,12 +339,12 @@ FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const
|
||||
/* Positions contains the location of the begin of each of the glyphs, and the end of the last one. */
|
||||
this->positions = MallocT<float>(this->glyph_count * 2 + 2);
|
||||
this->positions[0] = x;
|
||||
this->positions[1] = 0;
|
||||
this->positions[1] = font->fc->GetAscender();
|
||||
|
||||
for (int i = 0; i < this->glyph_count; i++) {
|
||||
this->glyphs[i] = font->fc->MapCharToGlyph(chars[i]);
|
||||
this->positions[2 * i + 2] = this->positions[2 * i] + font->fc->GetGlyphWidth(this->glyphs[i]);
|
||||
this->positions[2 * i + 3] = 0;
|
||||
this->positions[2 * i + 3] = font->fc->GetAscender();
|
||||
this->glyph_to_char[i] = i;
|
||||
}
|
||||
}
|
||||
|
@ -900,7 +900,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow {
|
||||
void OnInit() override
|
||||
{
|
||||
/* Width of the legend blob. */
|
||||
this->legend_width = (FONT_HEIGHT_SMALL - ScaleFontTrad(1)) * 8 / 5;
|
||||
this->legend_width = (FONT_HEIGHT_SMALL - ScaleGUITrad(1)) * 8 / 5;
|
||||
}
|
||||
|
||||
void UpdateExcludedData()
|
||||
|
@ -1008,24 +1008,19 @@ STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Check th
|
||||
|
||||
STR_GAME_OPTIONS_VIDEO_DRIVER_INFO :{BLACK}Current driver: {RAW_STRING}
|
||||
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interface size
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Select the interface element size to use
|
||||
STR_GAME_OPTIONS_GUI_SCALE_FRAME :{BLACK}Interface size
|
||||
STR_GAME_OPTIONS_GUI_SCALE_TOOLTIP :{BLACK}Drag slider to set interface size. Hold Ctrl for continuous adjustment
|
||||
STR_GAME_OPTIONS_GUI_SCALE_AUTO :{BLACK}Auto-detect size
|
||||
STR_GAME_OPTIONS_GUI_SCALE_AUTO_TOOLTIP :{BLACK}Check this box to detect interface size automatically
|
||||
|
||||
STR_GAME_OPTIONS_GUI_SCALE_BEVELS :{BLACK}Scale bevels
|
||||
STR_GAME_OPTIONS_GUI_SCALE_BEVELS_TOOLTIP :{BLACK}Check this box to scale bevels by interface size
|
||||
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(auto-detect)
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normal
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Double size
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Quad size
|
||||
|
||||
STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Font size
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Select the interface font size to use
|
||||
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detect)
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Double size
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Quad size
|
||||
STR_GAME_OPTIONS_GUI_SCALE_1X :1x
|
||||
STR_GAME_OPTIONS_GUI_SCALE_2X :2x
|
||||
STR_GAME_OPTIONS_GUI_SCALE_3X :3x
|
||||
STR_GAME_OPTIONS_GUI_SCALE_4X :4x
|
||||
STR_GAME_OPTIONS_GUI_SCALE_5X :5x
|
||||
|
||||
STR_GAME_OPTIONS_GRAPHICS :{BLACK}Graphics
|
||||
|
||||
|
@ -1159,7 +1159,7 @@ struct MessageHistoryWindow : Window {
|
||||
/* Months are off-by-one, so it's actually 8. Not using
|
||||
* month 12 because the 1 is usually less wide. */
|
||||
SetDParam(0, ConvertYMDToDate(ORIGINAL_MAX_YEAR, 7, 30));
|
||||
this->date_width = GetStringBoundingBox(STR_SHORT_DATE).width + ScaleFontTrad(5);
|
||||
this->date_width = GetStringBoundingBox(STR_SHORT_DATE).width + ScaleGUITrad(5);
|
||||
|
||||
size->height = 4 * resize->height + WidgetDimensions::scaled.framerect.Vertical(); // At least 4 lines are visible.
|
||||
size->width = std::max(200u, size->width); // At least 200 pixels wide.
|
||||
|
@ -756,7 +756,9 @@ int openttd_main(int argc, char *argv[])
|
||||
|
||||
/* Initialize the zoom level of the screen to normal */
|
||||
_screen.zoom = ZOOM_LVL_NORMAL;
|
||||
UpdateGUIZoom();
|
||||
|
||||
/* The video driver is now selected, now initialise GUI zoom */
|
||||
AdjustGUIZoom(false);
|
||||
|
||||
NetworkStartUp(); // initialize network-core
|
||||
|
||||
|
@ -173,7 +173,7 @@ void CoreTextFontCache::SetFontSize(int pixels)
|
||||
{
|
||||
if (pixels == 0) {
|
||||
/* Try to determine a good height based on the height recommended by the font. */
|
||||
int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
|
||||
int scaled_height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
pixels = scaled_height;
|
||||
|
||||
CFAutoRelease<CTFontRef> font(CTFontCreateWithFontDescriptor(this->font_desc.get(), 0.0f, nullptr));
|
||||
@ -197,11 +197,12 @@ void CoreTextFontCache::SetFontSize(int pixels)
|
||||
|
||||
/* Font height is minimum height plus the difference between the default
|
||||
* height for this font size and the small size. */
|
||||
int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
pixels = Clamp(std::min<int>(min_size, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE);
|
||||
int diff = scaled_height - ScaleGUITrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
/* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
|
||||
pixels = std::min(std::max(std::min<int>(min_size, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
|
||||
}
|
||||
} else {
|
||||
pixels = ScaleFontTrad(pixels);
|
||||
pixels = ScaleGUITrad(pixels);
|
||||
}
|
||||
this->used_size = pixels;
|
||||
|
||||
|
@ -392,7 +392,7 @@ void Win32FontCache::SetFontSize(FontSize fs, int pixels)
|
||||
{
|
||||
if (pixels == 0) {
|
||||
/* Try to determine a good height based on the minimal height recommended by the font. */
|
||||
int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs));
|
||||
int scaled_height = ScaleGUITrad(this->GetDefaultFontHeight(this->fs));
|
||||
pixels = scaled_height;
|
||||
|
||||
HFONT temp = CreateFontIndirect(&this->logfont);
|
||||
@ -405,14 +405,15 @@ void Win32FontCache::SetFontSize(FontSize fs, int pixels)
|
||||
|
||||
/* Font height is minimum height plus the difference between the default
|
||||
* height for this font size and the small size. */
|
||||
int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
pixels = Clamp(std::min(otm->otmusMinimumPPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE);
|
||||
int diff = scaled_height - ScaleGUITrad(this->GetDefaultFontHeight(FS_SMALL));
|
||||
/* Clamp() is not used as scaled_height could be greater than MAX_FONT_SIZE, which is not permitted in Clamp(). */
|
||||
pixels = std::min(std::max(std::min<int>(otm->otmusMinimumPPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height), MAX_FONT_SIZE);
|
||||
|
||||
SelectObject(dc, old);
|
||||
DeleteObject(temp);
|
||||
}
|
||||
} else {
|
||||
pixels = ScaleFontTrad(pixels);
|
||||
pixels = ScaleGUITrad(pixels);
|
||||
}
|
||||
this->used_size = pixels;
|
||||
|
||||
|
@ -57,22 +57,6 @@ static const StringID _autosave_dropdown[] = {
|
||||
INVALID_STRING_ID,
|
||||
};
|
||||
|
||||
static const StringID _gui_zoom_dropdown[] = {
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO,
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL,
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM,
|
||||
STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM,
|
||||
INVALID_STRING_ID,
|
||||
};
|
||||
|
||||
static const StringID _font_zoom_dropdown[] = {
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO,
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL,
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM,
|
||||
STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM,
|
||||
INVALID_STRING_ID,
|
||||
};
|
||||
|
||||
static Dimension _circle_size; ///< Dimension of the circle +/- icon. This is here as not all users are within the class of the settings window.
|
||||
|
||||
static const void *ResolveObject(const GameSettings *settings_ptr, const IntSettingDesc *sd);
|
||||
@ -158,14 +142,36 @@ static void AddCustomRefreshRates()
|
||||
std::copy(monitorRates.begin(), monitorRates.end(), std::inserter(_refresh_rates, _refresh_rates.end()));
|
||||
}
|
||||
|
||||
static const std::map<int, StringID> _scale_labels = {
|
||||
{ 100, STR_GAME_OPTIONS_GUI_SCALE_1X },
|
||||
{ 125, STR_NULL },
|
||||
{ 150, STR_NULL },
|
||||
{ 175, STR_NULL },
|
||||
{ 200, STR_GAME_OPTIONS_GUI_SCALE_2X },
|
||||
{ 225, STR_NULL },
|
||||
{ 250, STR_NULL },
|
||||
{ 275, STR_NULL },
|
||||
{ 300, STR_GAME_OPTIONS_GUI_SCALE_3X },
|
||||
{ 325, STR_NULL },
|
||||
{ 350, STR_NULL },
|
||||
{ 375, STR_NULL },
|
||||
{ 400, STR_GAME_OPTIONS_GUI_SCALE_4X },
|
||||
{ 425, STR_NULL },
|
||||
{ 450, STR_NULL },
|
||||
{ 475, STR_NULL },
|
||||
{ 500, STR_GAME_OPTIONS_GUI_SCALE_5X },
|
||||
};
|
||||
|
||||
struct GameOptionsWindow : Window {
|
||||
GameSettings *opt;
|
||||
bool reload;
|
||||
int gui_scale;
|
||||
|
||||
GameOptionsWindow(WindowDesc *desc) : Window(desc)
|
||||
{
|
||||
this->opt = &GetGameSettings();
|
||||
this->reload = false;
|
||||
this->gui_scale = _gui_scale;
|
||||
|
||||
AddCustomRefreshRates();
|
||||
|
||||
@ -264,24 +270,6 @@ struct GameOptionsWindow : Window {
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_GO_GUI_ZOOM_DROPDOWN: {
|
||||
*selected_index = _gui_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _gui_zoom + 1 : 0;
|
||||
const StringID *items = _gui_zoom_dropdown;
|
||||
for (int i = 0; *items != INVALID_STRING_ID; items++, i++) {
|
||||
list.emplace_back(new DropDownListStringItem(*items, i, i != 0 && _settings_client.gui.zoom_min > ZOOM_LVL_OUT_4X - i + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GO_FONT_ZOOM_DROPDOWN: {
|
||||
*selected_index = _font_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _font_zoom + 1 : 0;
|
||||
const StringID *items = _font_zoom_dropdown;
|
||||
for (int i = 0; *items != INVALID_STRING_ID; items++, i++) {
|
||||
list.emplace_back(new DropDownListStringItem(*items, i, false));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GO_BASE_GRF_DROPDOWN:
|
||||
list = BuildSetDropDownList<BaseGraphics>(selected_index, (_game_mode == GM_MENU));
|
||||
break;
|
||||
@ -304,8 +292,6 @@ struct GameOptionsWindow : Window {
|
||||
case WID_GO_CURRENCY_DROPDOWN: SetDParam(0, _currency_specs[this->opt->locale.currency].name); break;
|
||||
case WID_GO_AUTOSAVE_DROPDOWN: SetDParam(0, _autosave_dropdown[_settings_client.gui.autosave]); break;
|
||||
case WID_GO_LANG_DROPDOWN: SetDParamStr(0, _current_language->own_name); break;
|
||||
case WID_GO_GUI_ZOOM_DROPDOWN: SetDParam(0, _gui_zoom_dropdown[_gui_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _gui_zoom_cfg + 1 : 0]); break;
|
||||
case WID_GO_FONT_ZOOM_DROPDOWN: SetDParam(0, _font_zoom_dropdown[_font_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _font_zoom_cfg + 1 : 0]); break;
|
||||
case WID_GO_BASE_GRF_DROPDOWN: SetDParamStr(0, BaseGraphics::GetUsedSet()->name); break;
|
||||
case WID_GO_BASE_GRF_STATUS: SetDParam(0, BaseGraphics::GetUsedSet()->GetNumInvalid()); break;
|
||||
case WID_GO_BASE_SFX_DROPDOWN: SetDParamStr(0, BaseSounds::GetUsedSet()->name); break;
|
||||
@ -346,6 +332,10 @@ struct GameOptionsWindow : Window {
|
||||
DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, STR_BLACK_RAW_STRING);
|
||||
break;
|
||||
|
||||
case WID_GO_GUI_SCALE:
|
||||
DrawSliderWidget(r, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE, this->gui_scale, _scale_labels);
|
||||
break;
|
||||
|
||||
case WID_GO_BASE_SFX_VOLUME:
|
||||
DrawSliderWidget(r, 0, INT8_MAX, _settings_client.music.effect_vol, {});
|
||||
break;
|
||||
@ -486,6 +476,30 @@ struct GameOptionsWindow : Window {
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GO_GUI_SCALE:
|
||||
if (ClickSliderWidget(this->GetWidget<NWidgetBase>(widget)->GetCurrentRect(), pt, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE, this->gui_scale)) {
|
||||
if (!_ctrl_pressed) this->gui_scale = ((this->gui_scale + 12) / 25) * 25;
|
||||
this->SetWidgetDirty(widget);
|
||||
}
|
||||
|
||||
if (click_count > 0) this->mouse_capture_widget = widget;
|
||||
break;
|
||||
|
||||
case WID_GO_GUI_SCALE_AUTO:
|
||||
{
|
||||
if (_gui_scale_cfg == -1) {
|
||||
_gui_scale_cfg = _gui_scale;
|
||||
this->SetWidgetLoweredState(WID_GO_GUI_SCALE_AUTO, false);
|
||||
} else {
|
||||
_gui_scale_cfg = -1;
|
||||
this->SetWidgetLoweredState(WID_GO_GUI_SCALE_AUTO, true);
|
||||
if (AdjustGUIZoom(false)) ReInitAllWindows(true);
|
||||
this->gui_scale = _gui_scale;
|
||||
}
|
||||
this->SetWidgetDirty(widget);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GO_BASE_SFX_VOLUME:
|
||||
case WID_GO_BASE_MUSIC_VOLUME: {
|
||||
byte &vol = (widget == WID_GO_BASE_MUSIC_VOLUME) ? _settings_client.music.music_vol : _settings_client.music.effect_vol;
|
||||
@ -517,6 +531,19 @@ struct GameOptionsWindow : Window {
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseLoop() override
|
||||
{
|
||||
if (_left_button_down || this->gui_scale == _gui_scale) return;
|
||||
|
||||
_gui_scale_cfg = this->gui_scale;
|
||||
|
||||
if (AdjustGUIZoom(false)) {
|
||||
ReInitAllWindows(true);
|
||||
this->SetWidgetLoweredState(WID_GO_GUI_SCALE_AUTO, false);
|
||||
this->SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base media set.
|
||||
* @param index the index of the media set
|
||||
@ -576,36 +603,6 @@ struct GameOptionsWindow : Window {
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GO_GUI_ZOOM_DROPDOWN: {
|
||||
int8 new_zoom = index > 0 ? ZOOM_LVL_OUT_4X - index + 1 : ZOOM_LVL_CFG_AUTO;
|
||||
if (new_zoom != _gui_zoom_cfg) {
|
||||
GfxClearSpriteCache();
|
||||
_gui_zoom_cfg = new_zoom;
|
||||
UpdateGUIZoom();
|
||||
UpdateCursorSize();
|
||||
SetupWidgetDimensions();
|
||||
UpdateAllVirtCoords();
|
||||
FixTitleGameZoom();
|
||||
ReInitAllWindows(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GO_FONT_ZOOM_DROPDOWN: {
|
||||
int8 new_zoom = index > 0 ? ZOOM_LVL_OUT_4X - index + 1 : ZOOM_LVL_CFG_AUTO;
|
||||
if (new_zoom != _font_zoom_cfg) {
|
||||
GfxClearSpriteCache();
|
||||
_font_zoom_cfg = new_zoom;
|
||||
UpdateGUIZoom();
|
||||
ClearFontCache();
|
||||
LoadStringWidthTable();
|
||||
SetupWidgetDimensions();
|
||||
UpdateAllVirtCoords();
|
||||
ReInitAllWindows(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_GO_BASE_GRF_DROPDOWN:
|
||||
this->SetMediaSet<BaseGraphics>(index);
|
||||
break;
|
||||
@ -637,6 +634,7 @@ struct GameOptionsWindow : Window {
|
||||
this->SetWidgetDisabledState(WID_GO_VIDEO_VSYNC_BUTTON, !_video_hw_accel);
|
||||
#endif
|
||||
|
||||
this->SetWidgetLoweredState(WID_GO_GUI_SCALE_AUTO, _gui_scale_cfg == -1);
|
||||
this->SetWidgetLoweredState(WID_GO_GUI_SCALE_BEVEL_BUTTON, _settings_client.gui.scale_bevels);
|
||||
|
||||
bool missing_files = BaseGraphics::GetUsedSet()->GetNumMissing() == 0;
|
||||
@ -664,16 +662,6 @@ static const NWidgetPart _nested_game_options_widgets[] = {
|
||||
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_AUTOSAVE_FRAME, STR_NULL),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_AUTOSAVE_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_GUI_ZOOM_FRAME, STR_NULL),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_GUI_ZOOM_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP), SetFill(1, 0),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetDataTip(STR_GAME_OPTIONS_GUI_SCALE_BEVELS, STR_NULL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_GUI_SCALE_BEVEL_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_GUI_SCALE_BEVELS_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_CURRENCY_UNITS_FRAME, STR_NULL),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_CURRENCY_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_CURRENCY_UNITS_DROPDOWN_TOOLTIP), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
@ -683,8 +671,20 @@ static const NWidgetPart _nested_game_options_widgets[] = {
|
||||
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_LANGUAGE, STR_NULL),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_LANG_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_LANGUAGE_TOOLTIP), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_FONT_ZOOM, STR_NULL),
|
||||
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_FONT_ZOOM_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP), SetFill(1, 0),
|
||||
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_GUI_SCALE_FRAME, STR_NULL),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
|
||||
NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_GUI_SCALE), SetMinimalSize(67, 0), SetMinimalTextLines(1, 12 + WidgetDimensions::unscaled.vsep_normal, FS_SMALL), SetFill(0, 0), SetDataTip(0x0, STR_GAME_OPTIONS_GUI_SCALE_TOOLTIP),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetDataTip(STR_GAME_OPTIONS_GUI_SCALE_AUTO, STR_NULL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_GUI_SCALE_AUTO), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_GUI_SCALE_AUTO_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetDataTip(STR_GAME_OPTIONS_GUI_SCALE_BEVELS, STR_NULL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_GUI_SCALE_BEVEL_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_GUI_SCALE_BEVELS_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
|
@ -1188,7 +1188,7 @@ void SmallMapWindow::RebuildColourIndexIfNecessary()
|
||||
}
|
||||
|
||||
/* Width of the legend blob. */
|
||||
this->legend_width = (FONT_HEIGHT_SMALL - ScaleFontTrad(1)) * 8 / 5;
|
||||
this->legend_width = (FONT_HEIGHT_SMALL - ScaleGUITrad(1)) * 8 / 5;
|
||||
|
||||
/* The width of a column is the minimum width of all texts + the size of the blob + some spacing */
|
||||
this->column_width = min_width + this->legend_width + WidgetDimensions::scaled.framerect.Horizontal();
|
||||
|
@ -517,14 +517,14 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
||||
return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator, encoder);
|
||||
}
|
||||
|
||||
if (sprite->type == ST_FONT && ZOOM_LVL_FONT != ZOOM_LVL_NORMAL) {
|
||||
/* Make ZOOM_LVL_NORMAL be ZOOM_LVL_FONT */
|
||||
sprite[ZOOM_LVL_NORMAL].width = sprite[ZOOM_LVL_FONT].width;
|
||||
sprite[ZOOM_LVL_NORMAL].height = sprite[ZOOM_LVL_FONT].height;
|
||||
sprite[ZOOM_LVL_NORMAL].x_offs = sprite[ZOOM_LVL_FONT].x_offs;
|
||||
sprite[ZOOM_LVL_NORMAL].y_offs = sprite[ZOOM_LVL_FONT].y_offs;
|
||||
sprite[ZOOM_LVL_NORMAL].data = sprite[ZOOM_LVL_FONT].data;
|
||||
sprite[ZOOM_LVL_NORMAL].colours = sprite[ZOOM_LVL_FONT].colours;
|
||||
if (sprite->type == ST_FONT && ZOOM_LVL_GUI != ZOOM_LVL_NORMAL) {
|
||||
/* Make ZOOM_LVL_NORMAL be ZOOM_LVL_GUI */
|
||||
sprite[ZOOM_LVL_NORMAL].width = sprite[ZOOM_LVL_GUI].width;
|
||||
sprite[ZOOM_LVL_NORMAL].height = sprite[ZOOM_LVL_GUI].height;
|
||||
sprite[ZOOM_LVL_NORMAL].x_offs = sprite[ZOOM_LVL_GUI].x_offs;
|
||||
sprite[ZOOM_LVL_NORMAL].y_offs = sprite[ZOOM_LVL_GUI].y_offs;
|
||||
sprite[ZOOM_LVL_NORMAL].data = sprite[ZOOM_LVL_GUI].data;
|
||||
sprite[ZOOM_LVL_NORMAL].colours = sprite[ZOOM_LVL_GUI].colours;
|
||||
}
|
||||
|
||||
return encoder->Encode(sprite, allocator);
|
||||
|
@ -163,7 +163,7 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoID typ
|
||||
const CargoSpec *cs = CargoSpec::Get(type);
|
||||
if (!cs->IsValid()) return;
|
||||
|
||||
int padding = ScaleFontTrad(1);
|
||||
int padding = ScaleGUITrad(1);
|
||||
int width = right - left;
|
||||
int colour = cs->rating_colour;
|
||||
TextColour tc = GetContrastColour(colour);
|
||||
@ -177,7 +177,7 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoID typ
|
||||
} else {
|
||||
/* Draw a (scaled) one pixel-wide bar of additional cargo meter, useful
|
||||
* for stations with only a small amount (<=30) */
|
||||
uint rest = ScaleFontTrad(amount) / 5;
|
||||
uint rest = ScaleGUITrad(amount) / 5;
|
||||
if (rest != 0) {
|
||||
GfxFillRect(left, y + height - rest, left + padding - 1, y + height, colour);
|
||||
}
|
||||
@ -391,7 +391,7 @@ public:
|
||||
}
|
||||
|
||||
case WID_STL_LIST:
|
||||
resize->height = std::max(FONT_HEIGHT_NORMAL, FONT_HEIGHT_SMALL + ScaleFontTrad(3));
|
||||
resize->height = std::max(FONT_HEIGHT_NORMAL, FONT_HEIGHT_SMALL + ScaleGUITrad(3));
|
||||
size->height = padding.height + 5 * resize->height;
|
||||
|
||||
/* Determine appropriate width for mini station rating graph */
|
||||
|
@ -334,21 +334,12 @@ max = UINT32_MAX
|
||||
cat = SC_EXPERT
|
||||
|
||||
[SDTG_VAR]
|
||||
name = ""gui_zoom""
|
||||
type = SLE_INT8
|
||||
var = _gui_zoom_cfg
|
||||
def = ZOOM_LVL_CFG_AUTO
|
||||
min = ZOOM_LVL_CFG_AUTO
|
||||
max = ZOOM_LVL_OUT_4X
|
||||
cat = SC_BASIC
|
||||
|
||||
[SDTG_VAR]
|
||||
name = ""font_zoom""
|
||||
type = SLE_INT8
|
||||
var = _font_zoom_cfg
|
||||
def = ZOOM_LVL_CFG_AUTO
|
||||
min = ZOOM_LVL_CFG_AUTO
|
||||
max = ZOOM_LVL_OUT_4X
|
||||
name = ""gui_scale""
|
||||
type = SLE_INT32
|
||||
var = _gui_scale_cfg
|
||||
def = -1
|
||||
min = -1
|
||||
max = MAX_INTERFACE_SCALE
|
||||
cat = SC_BASIC
|
||||
|
||||
[SDTG_BOOL]
|
||||
|
@ -1269,7 +1269,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel
|
||||
/** Screen the window is on changed. */
|
||||
- (void)windowDidChangeBackingProperties:(NSNotification *)notification
|
||||
{
|
||||
bool did_adjust = AdjustGUIZoom();
|
||||
bool did_adjust = AdjustGUIZoom(true);
|
||||
|
||||
/* Reallocate screen buffer if necessary. */
|
||||
driver->AllocateBackingStore();
|
||||
|
@ -167,15 +167,13 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a suggested default GUI zoom taking screen DPI into account.
|
||||
* Get a suggested default GUI scale taking screen DPI into account.
|
||||
*/
|
||||
virtual ZoomLevel GetSuggestedUIZoom()
|
||||
virtual int GetSuggestedUIScale()
|
||||
{
|
||||
float dpi_scale = this->GetDPIScale();
|
||||
|
||||
if (dpi_scale >= 3.0f) return ZOOM_LVL_NORMAL;
|
||||
if (dpi_scale >= 1.5f) return ZOOM_LVL_OUT_2X;
|
||||
return ZOOM_LVL_OUT_4X;
|
||||
return Clamp(dpi_scale * 100, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
|
||||
}
|
||||
|
||||
virtual const char *GetInfoString() const
|
||||
|
@ -675,7 +675,7 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
}
|
||||
|
||||
case WM_DPICHANGED: {
|
||||
auto did_adjust = AdjustGUIZoom();
|
||||
auto did_adjust = AdjustGUIZoom(true);
|
||||
|
||||
/* Resize the window to match the new DPI setting. */
|
||||
RECT *prcNewWindow = (RECT *)lParam;
|
||||
|
@ -19,7 +19,8 @@ enum GameOptionsWidgets {
|
||||
WID_GO_LANG_DROPDOWN, ///< Language dropdown.
|
||||
WID_GO_RESOLUTION_DROPDOWN, ///< Dropdown for the resolution.
|
||||
WID_GO_FULLSCREEN_BUTTON, ///< Toggle fullscreen.
|
||||
WID_GO_GUI_ZOOM_DROPDOWN, ///< Dropdown for the GUI zoom level.
|
||||
WID_GO_GUI_SCALE, ///< GUI Scale slider.
|
||||
WID_GO_GUI_SCALE_AUTO, ///< Autodetect GUI scale button.
|
||||
WID_GO_GUI_SCALE_BEVEL_BUTTON, ///< Toggle for chunky bevels.
|
||||
WID_GO_BASE_GRF_DROPDOWN, ///< Use to select a base GRF.
|
||||
WID_GO_BASE_GRF_STATUS, ///< Info about missing files etc.
|
||||
@ -35,7 +36,6 @@ enum GameOptionsWidgets {
|
||||
WID_GO_BASE_MUSIC_STATUS, ///< Info about corrupted files etc.
|
||||
WID_GO_BASE_MUSIC_TEXTFILE, ///< Open base music readme, changelog (+1) or license (+2).
|
||||
WID_GO_BASE_MUSIC_DESCRIPTION = WID_GO_BASE_MUSIC_TEXTFILE + TFT_END, ///< Description of selected base music set.
|
||||
WID_GO_FONT_ZOOM_DROPDOWN, ///< Dropdown for the font zoom level.
|
||||
WID_GO_VIDEO_ACCEL_BUTTON, ///< Toggle for video acceleration.
|
||||
WID_GO_VIDEO_VSYNC_BUTTON, ///< Toggle for video vsync.
|
||||
WID_GO_REFRESH_RATE_DROPDOWN, ///< Dropdown for all available refresh rates.
|
||||
|
@ -116,27 +116,7 @@ static inline int ScaleSpriteTrad(int value)
|
||||
*/
|
||||
static inline int ScaleGUITrad(int value)
|
||||
{
|
||||
return UnScaleGUI(value * ZOOM_LVL_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Short-hand to apply font zoom level.
|
||||
* @param value Pixel amount at #ZOOM_LVL_BEGIN (full zoom in).
|
||||
* @return Pixel amount at #ZOOM_LVL_FONT (current interface size).
|
||||
*/
|
||||
static inline int UnScaleFont(int value)
|
||||
{
|
||||
return UnScaleByZoom(value, ZOOM_LVL_FONT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale traditional pixel dimensions to Font zoom level.
|
||||
* @param value Pixel amount at #ZOOM_LVL_BASE (traditional "normal" interface size).
|
||||
* @return Pixel amount at #ZOOM_LVL_FONT (current interface size).
|
||||
*/
|
||||
static inline int ScaleFontTrad(int value)
|
||||
{
|
||||
return UnScaleFont(value * ZOOM_LVL_BASE);
|
||||
return value * _gui_scale / 100;
|
||||
}
|
||||
|
||||
#endif /* ZOOM_FUNC_H */
|
||||
|
@ -15,8 +15,6 @@
|
||||
static uint const ZOOM_LVL_SHIFT = 2;
|
||||
static uint const ZOOM_LVL_BASE = 1 << ZOOM_LVL_SHIFT;
|
||||
|
||||
static const int8 ZOOM_LVL_CFG_AUTO = -1;
|
||||
|
||||
/** All zoom levels we know. */
|
||||
enum ZoomLevel : byte {
|
||||
/* Our possible zoom-levels */
|
||||
@ -50,12 +48,13 @@ enum ZoomLevel : byte {
|
||||
};
|
||||
DECLARE_POSTFIX_INCREMENT(ZoomLevel)
|
||||
|
||||
extern int8 _gui_zoom_cfg;
|
||||
extern int8 _font_zoom_cfg;
|
||||
extern int _gui_scale;
|
||||
extern int _gui_scale_cfg;
|
||||
|
||||
extern ZoomLevel _gui_zoom;
|
||||
extern ZoomLevel _font_zoom;
|
||||
#define ZOOM_LVL_GUI (_gui_zoom)
|
||||
#define ZOOM_LVL_FONT (_font_zoom)
|
||||
|
||||
static const int MIN_INTERFACE_SCALE = 100;
|
||||
static const int MAX_INTERFACE_SCALE = 500;
|
||||
|
||||
#endif /* ZOOM_TYPE_H */
|
||||
|
Loading…
Reference in New Issue
Block a user