Pull to refresh

Программирование под Windows CE на C++ с помощью Embedded Visual C++, часть 2.

Reading time 3 min
Views 4.1K
Программирование под Windows CE на C++ с помощью Embedded Visual C++, часть 2.

Продолжение 1-й части.


Под Win CE есть аналог InitCommonControls() и называется он SHInitExtraControls(). Лучше всего один раз вызвать эту функцию при старте приложения, чтобы всякие дополнительные контролы работали в вашем приложении.

Всплывающая клавиатура на КПК называется SIP (расш. Stupid Input Panel). Это прямоугольник, который отнимает некоторую часть видимого экрана. Понять сколько осталось можно так (не забудьте включить aygshell.h и прилинковать aygshell.lib):

SIPINFO si;
memset (&si, 0, sizeof (si));
si.cbSize = sizeof (si);
SHSipInfo(SPI_GETSIPINFO, 0, (PVOID)&si, FALSE);
bool SIP_visible = si.fdwFlags & SIPF_ON;
if (!SIP_visible) si.rcVisibleDesktop.bottom = si.rcSipRect.bottom;
return si.rcVisibleDesktop;


Можно самому прятать и показывать SIP (например, когда контрол edit получает фокус, разумно показать SIP для ввода данных):

void CloseSIP()
{
#ifdef UNDER_CE
SHSipPreference(MainForm()->hwnd, SIP_DOWN);
#endif
}

void OpenSIP()
{
#ifdef UNDER_CE
SHSipPreference(MainForm()->hwnd, SIP_UP);
#endif
}


Вот еще интересный кусок кода, который помогает SIP-у сохранять свое состояние когда наши окна получают WM_ACTIVATE:

case WM_ACTIVATE:
if (wp != WA_INACTIVE) {
#ifdef UNDER_CE
if (control == ActiveForm() && control->FullScreen)
SHHandleWMActivate(control->hwnd, wp, lp, &sai, 0);
#endif



Когда в системе что-то меняется она присылает сообщение WM_SETTINGCHANGE:

case WM_SETTINGCHANGE:
#ifdef UNDER_CE
if (MainForm())
SHHandleWMSettingChange(MainForm()->hwnd, wp, lp, &sai);
if (wp == SETTINGCHANGE_RESET)
Application->GetScreen();
#endif
break;


Много есть на платформе Windows Mobile дурац… извините, странных идей. В моем личном хит параде первое место по идиотизму занимает кнопка в правом верхнем углу. По умолчанию поведение этой кнопки такое: она не закрывает приложение (хотя там нарисован крестик), а убирает его с переднего плана. Причем у пользователя нет мало-мальски приличного способа его быстро отыскать и открыть кроме как лезть глубоко в список активных процессов или снова искать и запускать это приложение. Все это якобы должно экономить время запуска часто используемых приложений. Автор этой идеи может лично у меня получить шоколадную медаль «За отменный идиотизм»!

Итак, мы превратим этот идиотский крестик в чудесную кнопочку с надписью “OK” вот так:

void AllowToCloseWindow(HWND hwnd)
{
#ifdef UNDER_CE
ModifyStyle(0, WS_CAPTION | WS_CHILD);
SetExStyle(WS_EX_CAPTIONOKBTN);
SHDoneButton(hwnd, SHDB_SHOW);
#else
ModifyStyle(WS_SYSMENU);
#endif
}


Разумеется, не я первый догадался про идиотизм крестика. Нашлись умельцы, которые выпускают пакет, превращающий Windows Mobile в iPhone (шутка). Так вот иногда они перепрограммируют поведение крестика. Будьте к этому готовы, приложение может получать в главное окно сообщение WM_CLOSE или еще что-нибудь в момент нажатия на крестик. Спросите у разработчиков что именно!

Следующая богатая тема состоит в том, что у КПК бывают (в основном) два вида экранов: 320x240 и 640x480. При этом они _одинакового_ размера. То есть у них разный DPI (кол-во точек на квадратный дюйм). Чтобы работать с 640x480 надо сначала объяснить системе что Вы в курсе. Делается это включением в проект ресурса:

HI_RES_AWARE CEUX DISCARDABLE
BEGIN
0x0001
END


Если это не сделать, то даже на КПК с 640x480 Ваше приложение будет запущено в режиме, имитирующем 320x240. Теперь при запуске надо понять имеем ли мы дело с большим экраном:

bool HiRes()
{
return GetSystemMetrics(SM_CXSCREEN) >= 640 || GetSystemMetrics(SM_CYSCREEN) >= 640;
}


Однако этого мало. Нам-то нужно еще и DPI посчитать:

double SCALE = 1.0;
bool HighDPI(int dpi = 130)
{
if (DPI == 0)
{
HDC hdc = GetDC(0);
int DPI = GetDeviceCaps(hdc, LOGPIXELSX);
bool result = DPI >= dpi;
ReleaseDC(0, hdc);
SCALE = DPI / 96.0;
return result;
}
return DPI >= dpi;
}


И запомнить в SCALE множитель масштабирования. И теперь можно использовать следующие 2 функции:

int Descale(int a)
{
return int(a / SCALE);
}

int Scale(int a)
{
return int(a * SCALE);
}

Теперь при рисовании на канве все координаты переводятся в фактические с помощью Scale(), а при получении мышиных сообщений все координаты переводятся обратно с помощью Descale().

Продолжение последует!

Tags:
Hubs:
+9
Comments 12
Comments Comments 12

Articles