Языки программирования – 4
Вначале о том, что имеется в момент когда появляется Windows.
Во-первых, довольно неудобно то, что можно работать только с одной программой. Действительно, писать документ периодически заглядывая в цифры отчётности намного удобнее.
Во-вторых, программисты пользуются теми возможностями, которые дают им языки. Проблема количества запятых немного надуманная для обычного Си. Уже в нём есть возможность делать так называемые структуры, т.е описать например структуру «точка» со свойствами из двух координат и цвета. На практике этим не так часто пользуются.
Программисты – люди. У них, как и полагается, масса недостатков. Мало кто, например, склонен писать комментарии. Это лишнее нажимание кнопок и всегда есть детская надежда, что больше этот текст никто никогда ни читать ни редактировать не будет.
Постоянно требуются какие-то изменения. Может и надо было предусмотреть передачу параметров функции в виде структуры. Но когда функцию первый раз писали параметров было 3. Только потом их стало 10. И здесь неплохо бы как-то принципиально поменять технологию чтобы проще было работать несмотря и вопреки человеческой природе.
Это было про проблемы. Теперь про технологии и идеи.
Я уже писала, что нет никакого текстового режима с буквами. Есть точки на экране. Символы текстового режима - это всего лишь группировка точек в ряды и колонки. Но если на экране есть символ в 8 на 8 точек, то почему не разместить на нём что-то значительно более сложное по форме и поведению? Окно это такая же картинка на экране как и символ. Оно, конечно, сложнее. С ним можно работать, меняя размеры, например. Но по сути это ещё один вид изображения на экране, искусственной группировки разноцветных точек.
Несколько программ, работающих одновременно? В монопольном режиме в DOS-е работает только запущенная программа, которой и занимается интерпретатор машинных кодов. А это далеко не всё, чем занимается компьютер. Таймер в это время продолжает плюсовать миллисекунды. Буфер клавиатуры продолжает наполняться нажимаемыми символами даже если программа повисла и перестала их считывать. Более того сидящий в памяти русификатор будет заниматься подменой букв, о чём и не подозревает запущенная программа. Байты от мыши постоянно сыплются туда, куда положено, любой может брать и что-то делать. На этот момент уже отработано взаимодействие с разнообразными устройствами, подключенными к компьютеру. Все они имеют те самые драйвера, которые и позволяют с ними работать И это всё одновременно работает. Так почему бы ни сделать множество программы какими-то аналогами множества устройств, подключенных к компьютеру? Они все по очереди будут занимать машинное время, информация от них всех будет копиться и последовательно выполняться. Далее вопрос только в быстродействии того, что обрабатывает.
Следующее и логичное: когда нечто разрастается и усложняется становится невозможно разбираться во всех деталях работы какого-то подразделения. Не важно как токарь обтачивает болванку. Важно чтобы обточенное попадало на склад.
Подразделение это логический объект с какими-то свойствам (сколько людей, например) и функциями (отгружать обточенные болванки, например).
И при общении с калькулятором не надо знать, как он внутри устроен. Важно знать как ввести цифры, где находится кнопка «=», по которой можно получить результат и где будет нарисована цифра.
Короче было много причин по которым программирование стало объектно-ориентированным, т.е произошел переход от работы с простой информацией типа чиста, строки, даты к более сложной и структурированной. Если смотреть всю историю, то это нечто очередное и естественное. Напомню, что началось всё с того, что мало стало нуля и единицы в бите, потребовался хотя бы байт для передачи информации. А теперь для того же потребовался сложный ОБЪЕКТ.
Как и драйвер мыши объект имеет свойства (координаты положения на экране), методы (показать, скрыть курсор мыши) и события (нажата какой-то кнопка). Всё это есть у любого объекта.
Программа в Windows – тоже объект, о котором можно получить информацию, который выполняет какие-то действия, который сообщает системе о том, что у него произошло событие на которое надо как-то отреагировать. Либо же этот объект сам же и реагирует на какое-то действие пользователя (как обработчик прерывания, предусмотренный в операционной системе).
Программа в Windows это то, у чего всегда есть «окно». Окно может быть невидимым, но оно есть.
Но обычное окно это нечто примерно такое:
http://dims.karelia.ru/win32/
Окно приложения может содержать строку заголовка title bar (1), строку меню menu bar (2), системное меню system menu (3), кнопку сворачивания окна minimize box (4), кнопку разворачивания окна maximize box (5), рамку изменения размеров sizing border (6), клиентскую область client area (7), горизонтальную и вертикальную полосы прокрутки scroll bars (8):
… Обращаю внимание на то, что в середине белый прямоугольник. Рисовать на нём можно что угодно. Это и делается. Даже когда по экрану вверх перемещаются буквы это всего лишь перенос части экрана с буками вверх (одной картинкой, не разбираясь) и рисование внизу каких-то других букв (т.е следующей строки).
Ниже будет текст программы текстового редактора. Он короткий. Короткий он потому, что почти всё сложное засунуто в описание объектов в отдельных файлах.
В примере четыре функции.
Первая - _tWinMain – начало программы, команды которые начинают обрабатываться при запуске. В этой функции рисуется окно (w.Show), добавляются элементы меню окна типа «Файл», «Правка», «Поиск» на картинке выше (CreateWinMenu(w.hWnd);), а дальше бесконечный цикл ожидания каких-то событий на которые надо реагировать изменением того, что нарисовано на окне.
Третья - WndProc – обработчик событий.
Поскольку это текстовый редактор надо понять сколько букв будет помещаться на окне конкретного размера. Это и делается при создании окна, событии WM_CREATE.
WM_SIZE:- изменение размера. При одном размере на кнопку «стрелка вправо» надо реагировать перемещением курсора вправо, а при другом (когда видимая область закончилась) перемещением влево всех букв на экране. Поэтому нужно знать какие размеры у окна, отслеживать изменения и перерисовывать содержание окна.
Получение и потеря фокуса (WM_SETFOCUS, WM_KILLFOCUS)/ Окно становится активным (получает фокус) когда по нему щёлкают мышью и перестаёт быть активным (теряет фокус) когда щелкают по какому-то другому окну. Текстовому редактору логично реагировать на потерю фокуса выключением курсора, это и делается.
Нажатие кнопок – стрелок на экране (WM_KEYDOWN) должно вызывать перемещение по нему курсора. Этим и занимается метод d.MoveCaret описанного в отдельном файле объекта d (doc- документ). И уже его проблемы проконтролировать чтобы курсор не поехал влево когда он на первой букве строки или вниз когда он на последней строке документа.
Нажатие любой кнопки на клавиатуре - WM_CHAR. Если нажато Ctrl+0 – вызов функции открытия файла. Вставка символа в текст если это буква, цифра или знаки препинания. Разбирается в нажатым функция d.InsertChar(hWnd,wParam). Она и буквы вставляет, она и символы удаляет по кнопке Del.
Изменение положения полос прокрутки WM_VSCROLL, WM_HSCROLL. Текст в экране при этом тоже должен поменяться. А считать с какой строки показывать файл если прокрутка установлена на 30% от начала файла будет функция d.UpdateScroll( hWnd, message, LOWORD(wParam)).
Тексt editor/cpp:
// editor.cpp: определяет точку входа для приложения.
#include "stdafx.h"
#include "editor.h"
#include "wnd.h"
#include "doc.h"
#include <Commdlg.h>
#include "ST.h"
#include "KBD.h"
using namespace ST;
using namespace KBD;
// Глобальные переменные (классы окна (wnd) и документа (doc) )
wnd w;
doc d;
// Прототипы функциий (список всех написанных):
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateWinMenu(HWND hWnd);
void OpenFile(HWND hWnd);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
wchar_t * lpCmdLine,
int nCmdShow)
{
// Для цикла обработки сообщений (от клавиатуры, миши и т.д.):
MSG msg;
HACCEL hAccelTable;
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDI_EDITOR));
// Создание и вывод на окран окна:
w.Show(L"Редактор",hInstance,nCmdShow,WndProc,WS_OVERLAPPEDWINDOW|WS_VSCROLL|WS_HSCROLL);
// Создание меню окна:
CreateWinMenu(w.hWnd);
// Открытие файла, если он был передан через параметр:
if (ST::sLen(lpCmdLine)>0)
d.OpenFile(lpCmdLine,w.hWnd);
else
{
d.lines.push_back(wstring(L"")); // Для корректировки пустого одна строка
d.num_lines.push_back(0);
d.del_bool.push_back(false);
}
// Цикл основного сообщения:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
// Создание меню окна:
// Расположение элементов и "горячие кнопки" стандартные по WIndows (для удобства, по привычке)
void CreateWinMenu(HWND hWnd)
{
HMENU hMainMenu,hFileMenu, hHelpMenu;
// Подменю "Файл":
hFileMenu=CreatePopupMenu();
AppendMenu(hFileMenu, MF_ENABLED| MF_STRING, 101,
L"Создать\tCtrl+N");
AppendMenu(hFileMenu, MF_ENABLED| MF_STRING, 102,
L"Открыть\tCtrl+O");
AppendMenu(hFileMenu, MF_ENABLED| MF_STRING, 103,
L"Сохранить\tCtrl+S");
AppendMenu(hFileMenu, MF_ENABLED| MF_STRING, 104,
L"Сохранить как");
AppendMenu(hFileMenu, MF_GRAYED| MF_SEPARATOR , NULL, // горизонтальная черта
L"-");
AppendMenu(hFileMenu, MF_ENABLED| MF_STRING, 199,
L"Выход\tAlt+F4");
// Подменю "Справка":
hHelpMenu=CreatePopupMenu();
AppendMenu(hHelpMenu, MF_ENABLED| MF_STRING, 999,
L"О программе");
// Основное меню:
hMainMenu = CreateMenu();
AppendMenu(hMainMenu, MF_ENABLED | MF_POPUP, (UINT) hFileMenu,L"Файл");
AppendMenu(hMainMenu, MF_ENABLED | MF_POPUP, (UINT) hHelpMenu,L"Справка");
// Прицепить меню к окну:
SetMenu(hWnd, hMainMenu);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm;
HFONT hOldFont;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hWnd);
// Чтобы все буквы были одной ширины ("1" и "Щ"):
d.hTextFont = (HFONT)GetStockObject(SYSTEM_FIXED_FONT);
hOldFont = (HFONT)SelectObject(hdc, d.hTextFont );
GetTextMetrics(hdc,&tm);
d.iWidthChar= tm.tmAveCharWidth; // Размеры символа
d.iHeightChar = tm.tmHeight+tm.tmExternalLeading;
ReleaseDC(hWnd,hdc);
break;
case WM_SIZE:
d.iWidthWindow = LOWORD(lParam);
d.iHeightWindow = HIWORD(lParam);
if (LOWORD(lParam)>0)
d.SetScroll(hWnd);
d.DocShowCaret(hWnd); // Вывод/скрытие курсора
break;
case WM_SETFOCUS:
CreateCaret(hWnd,NULL,5,d.iHeightChar);
d.DocShowCaret(hWnd);
break;
case WM_KILLFOCUS:
HideCaret(hWnd);
DestroyCaret();
break;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
d.MoveCaret(hWnd,wParam);
break;
case WM_CHAR:
if (KBD::IsCtrlKey('o',wParam))
{
OpenFile(hWnd);
break;
}
d.InsertChar(hWnd,wParam);
break;
case WM_VSCROLL:
d.UpdateScroll( hWnd, message, LOWORD(wParam));
break;
case WM_HSCROLL:
d.UpdateScroll( hWnd, message, LOWORD(wParam));
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case 102:
OpenFile(hWnd);
break;
case 199:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
hOldFont = (HFONT)SelectObject(hdc, d.hTextFont);
d.DrawText(hWnd,hdc,-1,-1);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void OpenFile(HWND hWnd)
{
d.OpenFile(L"",hWnd);
InvalidateRect(hWnd,NULL,true);
UpdateWindow(hWnd);
}
p/s
Справка по окну:
https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms633576(v=vs.85).aspx
|