Перейти к содержанию

Antonshka

Пользователи+
  • Постов

    410
  • Зарегистрирован

  • Посещение

  • Победитель дней

    16

Сообщения, опубликованные Antonshka

  1. В 29.07.2022 в 13:57, Xipho сказал:

    Эта серия вся дружелюбная. Годноты там немало )

    Я дочитал Head First...

    Ну как дочитал, первую половину книги я читал тщательно. Ничего не пропуская. А вторую, я читал уже на перемотке.

     

    Я не могу понять одно, - толь я туповат, толь авторы книги объясняют непонятно. Я начинаю читать очередную главу о паттерне, о его устройстве, назначении. Но ничего не понимаю. Снова читаю, снова не понимаю. Так я доходу до примера кода, который на Java (но ничего, он похож на С++). Переписываю его весь в Visual Studio, корректирую под С++. И тут мне сразу все становиться понятно. Стоит только взглянуть на общую картину кода. И так со всеми паттернами!

    Ладно еще на Github есть код примеров для этой книги, что помогло немного ускориться.

     

    Банду четырех я посмотрел еще раз, после прочтения Head First. Теперь она воспринимается по другому. Теперь понятно о чем они пытаются рассказать. Но читать я ее не буду, все по той же причине.

     

    Пока читал, прикидывал как можно применить паттерны к библиотеке. И сейчас прикидываю. Но ничего на ум не приходит. Хотя я хорошо понял их суть.

    Сейчас библиотека в основном использует композицию, а не наследование. То есть "стратегию".

    Из книги я понял что нужно программировать на интерфейсах а не на реализации, и что нужно отделять временное от постоянного. Посмотрю, где это можно применить. А так, даже в данный момент, архитектура библиотека мне кажется нормальной. Единственное, я не планировал делать ее кроссплатформенной. Чтобы не усложнять код.

  2. 13 часов назад, youneuoy сказал:

    в том и суть. В этом самом update вызывай draw и прочее🙂Подобное не так трудно уже будет обмазать паттернами и получить модный плюсовый код, который хипхо одобрит.

    это ты зря. Практика наше всё.

    Для практики нужна же теория. Ее у меня не достает.

    Книга Head First. это нечто. Такая дружелюбная и легкая. Не могу теперь переключиться на проект, пока ее не дочитаю. Ждал утра чтобы скорее продолжить читать. Обычно приходилось себя принуждать, более или менее, а тут, как бы идешь пообщаться с интересным позитивным человеком.

  3. В 27.07.2022 в 09:43, Xipho сказал:

    И снова у меня толком нет времени, чтобы тебе обстоятельно ответить, а давая контекст кусками, я создаю у тебя в голове впечатление противоречий. Это плохо. Давай так. Попробуй внимательно почитать про принципы SOLID. Если тебе что-то в них не будет понятно, пиши, постараюсь объяснить. На конкретных примерах кода в разных участках программы это делать сложно. Попутно - прочитать/перечитать книгу по паттернам от "банды четырех", внимательно вникая, как авторы рассуждают по ходу книги. Поинт на заметку - почему в винде каждый контрол является окном, туда же - в упомянутой книге почему в редакторе везде  и всюду во главе всего идет класс Glyph.

    Почитал "Банду четырех". Меня хватило на несколько часов.

    Таких заумно написанных книг я еще не читал. Абстракция на абстракции. При всем понимании необходимости в ее прочтении, я пасс

    Почитаю книгу Head First. Паттерны проектирования [2022]. Судя по первым страницам она попроще.

    Если и она не зайдет, то и не знаю как поступать.

  4. 11 часов назад, youneuoy сказал:

    переименуй везде draw в update и втыкай проверки внутрь. Или забей и пиши код дальше, у тебя ведь основной затык в общей архитектуре, а не в данном куске. Допиши либу и потом повтори снова, но уже по-человечески🙂 Не знаешь как спроектировать большую штуку - не проектируй, набей руку на написании обычных кусков кода сначала.

    "update" звучит немного размыто. Как "change", "modificate".

    Я решил отложить написание кода. Буду читать банду четырех. Посмотрю как они это делают.

  5. 1 час назад, Xipho сказал:

     А тут у тебя получается то, о чем я говорил - размазывание ответственности. По сути, родительскому компоненту незачем знать детали отрисовки дочерних компонентов. Каждый отвечает сам за себя.

    У меня сейчас именно такая схема о которой ты говоришь. Класс главного окна имеет своим полем объект класса TitleBar.  В оконной процедуре главное окно вызывает методы TitleBar. В определенных видах сообщений.

    Например, сообщение WM_PAINT, при нем главное окно вызывает этот метод DrawTitleBar() для TitleBar, проверив предварительно активен ли TitleBar.

    Спойлер
    VOID WindowPropsTitleBar::DrawTitleBar()
    {
      DrawTitleBarBackground();
      if (icon.isIconEnabled)
        icon.DrawIcon(windowOwner.CompatibleDC.compDC);
      if (text.isTextEnabled)
        text.DrawTitleText(windowOwner.CompatibleDC.compDC);
      if (buttonMinimize.isButtonStyleEnabled)
        buttonMinimize.DrawButton(windowOwner.CompatibleDC.compDC);
      if (buttonMaximize.isButtonStyleEnabled)
        buttonMaximize.DrawButton(windowOwner.CompatibleDC.compDC);
      buttonClose.DrawButton(windowOwner.CompatibleDC.compDC);
      BitBlt(windowOwner.CompatibleDC.origDC, titleBarRect.left, titleBarRect.top, titleBarRect.right - titleBarRect.left,
             titleBarRect.bottom - titleBarRect.top,	windowOwner.CompatibleDC.compDC, titleBarRect.left,	titleBarRect.top, SRCCOPY);
    }

     

    А TitleBar класс имеет своими полями объекты классов icon, text, и buttonMinimize, buttonMaximize, buttonClose.

    TitleBar рисует только свой background color. А все остальное рисование производится самими элементами.

    Весь вопрос в предварительной проверке активности элемента в родительском методе, перед его вызовом.

  6. 2 часа назад, Xipho сказал:

    Да, по поводу "как схлопнется три условия". Смотри, я увидел в твоем коде, что во всех этих трех условиях код практически одинаковый. Следовательно, я бы его схлопнул примерно как-то так (псевдокодом)

    void checkAndDrawTooltipItem(ToolTipItem &item) {
         if (item != NULL) {
              dynamicTextYOffset = item.rect.top;
              ...
              DrawToolTipTexts(item.data)
         }
    }

    Но при таком подходе не придется ли все методы класса называть "check" и далее имя операции? Все методы которые должны предварительно проверить состояние объекта.

    Двойное название как-то не так смотрится.

     

    Я бы остановился на таком варианте, если он допустим. Он мне вполне нравится.

    Спойлер
    VOID WindowPropsTitleBar::DrawTitleBar()
    {
      DrawTitleBarBackground();
      if (icon.isIconEnabled)
        icon.DrawIcon(windowOwner.CompatibleDC.compDC);
      if (text.isTextEnabled)
        text.DrawTitleText(windowOwner.CompatibleDC.compDC);
      if (buttonMinimize.isButtonStyleEnabled)
        buttonMinimize.DrawButton(windowOwner.CompatibleDC.compDC);
      if (buttonMaximize.isButtonStyleEnabled)
        buttonMaximize.DrawButton(windowOwner.CompatibleDC.compDC);
      buttonClose.DrawButton(windowOwner.CompatibleDC.compDC);
      BitBlt(windowOwner.CompatibleDC.origDC, titleBarRect.left, titleBarRect.top, titleBarRect.right - titleBarRect.left,
             titleBarRect.bottom - titleBarRect.top,	windowOwner.CompatibleDC.compDC, titleBarRect.left,	titleBarRect.top, SRCCOPY);
    }

     

     

  7. 1 час назад, Xipho сказал:

    Соответственно, я бы создал абстрактный класс ToolTipItem, и в нем бы свел все свойства, общие для всех компонентов тултипа, а отличающиеся части вывел бы в классы-наследники. В моем понимании (не заглядывая дальше в твои реализации), это бы сильно облегчило кодинг (почему я и намекаю на класс Glyph в книге "банды четырех") и расширение функционала.

    Я кстати такое сделал для TitleBar класса, после этого твоего совета

    В 25.07.2022 в 10:20, Xipho сказал:

    2, 3, 4 - вынести в отдельный метод, завести сущность, характеризующую рисование для этого метода, и сразу три условия схлопнется.

    Я сделал базовый класс для кнопок (закрыть, развернуть, свернуть), с общими для них методами и переменными. И сделал три наследника, так как есть в них некоторые отличительные особенности.

    Еще была мысль использовать шаблон Наблюдатель. Тогда бы можно было просто в цикле пройтись по кнопкам-подписчикам и вызвать у них базовый виртуальный метод рисования. Никакой проверки при этом проводить не нужно, так как подписчики только те кто в данный момент активен. Но я не стал такое делать.

    Для ToolTip планирую сделать такое же.

     

    1 час назад, Xipho сказал:

    Попробуй внимательно почитать про принципы SOLID.

    1 час назад, Xipho сказал:

     То, как ты сейчас пишешь код - у тебя получается помесь ООП и процедурного стиля программирования.

    Процедурный стиль видимо остался от опыта на LUA в CE. Я заметил что я пишу на нем потому что он проще для понимания. Но он не проще при последующих расширениях, как я заметил за последние два дня. Буду перестраиваться на ООП мышление.

     

    1 час назад, Xipho сказал:

     Если хочешь, можно на выходных найти время, состыковаться в дискорде голосом, чтобы проговорить основные моменты.

    Да, можно. Но сейчас у меня ничего не готово. У меня получается два шага вперед, и один назад. Потому так долго.

    Текущие глобальные ошибки мы обговорили, над ними я сейчас буду работать.

  8. 7 часов назад, youneuoy сказал:

    Чего ты на мелочах так стопоришься?

    Пытаюсь научиться писать код правильно и красиво.

     

    7 часов назад, youneuoy сказал:

    ага.

     

    10 часов назад, Xipho сказал:

    По-хорошему, в метода не должно быть "побочных эффектов". И если задача метода нарисовать кнопку, то проверять, надо ли ее вообще рисовать - выглядит как не его ответственность.

    Я немного не понял тогда, ведь было написано

    Спойлер
    В 25.07.2022 в 10:20, Xipho сказал:

    Пробегусь чисто поверхностно:

     

    image.png

    2, 3, 4 - вынести в отдельный метод, завести сущность, характеризующую рисование для этого метода, и сразу три условия схлопнется.

     

    Как схлопнется три условия, если лучше проверять условия не в методах рисования, и перед их вызовом. У меня здесь именно так и есть, проверка условий перед вызовом методов рисования.

  9. 38 минут назад, Xipho сказал:

    По-хорошему, в метода не должно быть "побочных эффектов". И если задача метода нарисовать кнопку, то проверять, надо ли ее вообще рисовать - выглядит как не его ответственность.

    То есть лучше делать вот так? С предварительной проверкой перед вызовом?

    Спойлер
    VOID WindowPropsTitleBar::DrawTitleBar()
    {
    	DrawTitleBarBackground();
    	if (isIconMustBeShown)
    		DrawIcon();
    	if (isTitleTextMustBeShown)
    		DrawTitleText();
    	if (hasMinimizeBtn)
    		DrawMinimizeButton();
    	if (hasMaximizeBtn)
    		DrawMaximizeButton();
    	DrawCloseButton();
    	BitBlt(windowOwner.CompatibleDC.origDC, titleBarRect.left,
    		titleBarRect.top,
    		titleBarRect.right - titleBarRect.left,
    		titleBarRect.bottom - titleBarRect.top,
    		windowOwner.CompatibleDC.compDC,
    		titleBarRect.left,
    		titleBarRect.top, SRCCOPY);
    }

     

    И по идее это еще лучше тем что нет лишних затрат на вызовы, ведь проверить переменную быстрее чем вызвать метод

  10. Подскажите, как лучше вызывать методы?

    Так, то есть сперва проверять активна ли кнопка или нет, а потом уже вызывать ее функцию рисования

    Спойлер
    VOID WindowPropsTitleBar::DrawTitleBar()
    {
    	DrawTitleBarBackground();
    	if (isIconMustBeShown)
    		DrawIcon();
    	if (isTitleTextMustBeShown)
    		DrawTitleText();
    	if (hasMinimizeBtn)
    		DrawMinimizeButton();
    	if (hasMaximizeBtn)
    		DrawMaximizeButton();
    	DrawCloseButton();
    	BitBlt(windowOwner.CompatibleDC.origDC, titleBarRect.left,
    		titleBarRect.top,
    		titleBarRect.right - titleBarRect.left,
    		titleBarRect.bottom - titleBarRect.top,
    		windowOwner.CompatibleDC.compDC,
    		titleBarRect.left,
    		titleBarRect.top, SRCCOPY);
    }

     

     

    или так, то есть проверку активности кнопки проводить в самом методе?

    Спойлер
    VOID WindowPropsTitleBar::DrawTitleBar()
    {
    	DrawTitleBarBackground();
    	DrawIcon();
    	DrawTitleText();
    	DrawMinimizeButton();
    	DrawMaximizeButton();
    	DrawCloseButton();
    	BitBlt(windowOwner.CompatibleDC.origDC, titleBarRect.left,
    		titleBarRect.top,
    		titleBarRect.right - titleBarRect.left,
    		titleBarRect.bottom - titleBarRect.top,
    		windowOwner.CompatibleDC.compDC,
    		titleBarRect.left,
    		titleBarRect.top, SRCCOPY);
    }

     

     

    Не могу решить как лучше. Особенно вот в таком случае

    Спойлер
    VOID WindowPropsTitleBar::OnOwnerWmLButtonDown(LPARAM& lParam)
    {
      POINT cursorPosRelative{ 0 };
      cursorPosRelative.x = GET_X_LPARAM(lParam);
      cursorPosRelative.y = GET_Y_LPARAM(lParam);
      if (sysMenu.IsLButtonDownOnSysMenuClickRect(cursorPosRelative))
        sysMenu.ShowSysMenu(TRUE, cursorPosRelative);
      if (cursorPosRelative.y < titleBarRect.bottom && IsCursorHoverTitleBarNotUsedArea(cursorPosRelative))
        DragWindowByTitleBarNotUsedArea();
    }

     

     

    Если поменяю на это, проведя проверку в самих методах

    Спойлер
    VOID WindowPropsTitleBar::OnOwnerWmLButtonDown(LPARAM& lParam)
    {
      POINT cursorPosRelative{ 0 };
      cursorPosRelative.x = GET_X_LPARAM(lParam);
      cursorPosRelative.y = GET_Y_LPARAM(lParam);
      sysMenu.ShowSysMenu(TRUE, cursorPosRelative);
      DragWindowByTitleBarNotUsedArea();
    }

     

    то будет выглядеть будто при нажатии правой кнопки мыши отобразится меню и начнется перетаскивание окна, одновременно.

    А если будет не два метода, а десять.

  11. 1 час назад, Xipho сказал:

    Для начала, надо понять, какую проблему ты решаешь. Сейчас ты поменял одну конструкцию на другую, стало чуть более читаемо, но суть не изменилась. Суть же в том, что у тебя несколько вложенных условных конструкций, что и ведет к снижению читаемости кода.

    Подумаю еще над этим.

  12. 1 час назад, youneuoy сказал:

    если в голову ничего не пришло, значит мало или плохо думал🙂

    Я много думал😊. Ничего более удобного я не смог придумать.

    Ну вот например, можно попробовать заменить это

    Спойлер
    while (std::getline(stringStream, currentString, L'\n'))
    {
      ExtTextOutW(CompatibleDC.compDC, 0, 0, ETO_OPAQUE, NULL, currentString.c_str(), currentString.length(), NULL);
      stringsLength += currentString.length();
      if (stringsLength == textLength - 1)
      {
        if (textData->text[textLength - 1] == L'\n')
        {
          dynamicTextYOffset += setting.titleTextLogFont.lfHeight;
          MoveToEx(CompatibleDC.compDC, titleTextRect.left, dynamicTextYOffset, NULL);
        }
      }
      else
      {
        if (textData->text[stringsLength] == L'\n')
        {
          stringsLength++;
          dynamicTextYOffset += setting.titleTextLogFont.lfHeight;
          MoveToEx(CompatibleDC.compDC, titleTextRect.left, dynamicTextYOffset, NULL);
        }
      }
    }

     

    на вот это

    Спойлер
    while (std::getline(stringStream, currentString, L'\n'))
    {
      ExtTextOutW(CompatibleDC.compDC, 0, 0, ETO_OPAQUE, NULL, currentString.c_str(), currentString.length(), NULL);
      stringsLength += currentString.length();
      if (IsAllTextObserved()) //if (stringsLength == textLength - 1)
      {
        if (IsLastTextCharacterHasNewLineChar()) // if (textData->text[textLength - 1] == L'\n')
        {
          ChangeCurrentPositionDC(); //dynamicTextYOffset += setting.titleTextLogFont.lfHeight;
           							// MoveToEx(CompatibleDC.compDC, titleTextRect.left, dynamicTextYOffset, NULL);
        }
      }
      else
      {
        if (IsCurrentTextPartHasNewLineCharAtTheEnd) //if (textData->text[stringsLength] == L'\n')
        {
          stringsLength++;
          ChangeCurrentPositionDC(); //dynamicTextYOffset += setting.titleTextLogFont.lfHeight;
           							// MoveToEx(CompatibleDC.compDC, titleTextRect.left, dynamicTextYOffset, NULL);
        }
      }
    }

     

     

    Не знаю, нормально так или нет.

  13. 1 час назад, Xipho сказал:

    Это же не мой репозиторий, это форк, посмотри внимательно ))

    image.png

    Понятно теперь.

     

    5 часов назад, Xipho сказал:

    1. Опять местами отсутствуют операторские скобки. Да, там, где в условии одно действие, они не являются необходимыми, но для упрощения читаемости кода их всё равно рекомендуется указывать

    Попробовал сейчас поставить скобки, - выглядит так себе.

    Без скобок

    Спойлер
    VOID WindowPropsTitleBar::DrawTitleBar()
    {
    	DrawTitleBarBackground();
    	if (isIconMustBeShown)
    		DrawIcon();
    	if (isTitleTextMustBeShown)
    		DrawTitleText();
    	if (hasMinimizeBtn)
    		DrawMinimizeButton();
    	if (hasMaximizeBtn)
    		DrawMaximizeButton();
    	DrawCloseButton();
    	BitBlt(windowOwner.CompatibleDC.origDC, titleBarRect.left,
    		titleBarRect.top,
    		titleBarRect.right - titleBarRect.left,
    		titleBarRect.bottom - titleBarRect.top,
    		windowOwner.CompatibleDC.compDC,
    		titleBarRect.left,
    		titleBarRect.top, SRCCOPY);
    }

     

     

    Со скобками

    Спойлер
    VOID WindowPropsTitleBar::DrawTitleBar()
    {
    	DrawTitleBarBackground();
    	if (isIconMustBeShown)
    	{
    		DrawIcon();
    	}
    	if (isTitleTextMustBeShown)
    	{
    		DrawTitleText();
    	}
    	if (hasMinimizeBtn)
    	{
    		DrawMinimizeButton();
    	}
    	if (hasMaximizeBtn)
    	{
    		DrawMaximizeButton();
    	}
    	DrawCloseButton();
    	BitBlt(windowOwner.CompatibleDC.origDC, titleBarRect.left,
    		titleBarRect.top,
    		titleBarRect.right - titleBarRect.left,
    		titleBarRect.bottom - titleBarRect.top,
    		windowOwner.CompatibleDC.compDC,
    		titleBarRect.left,
    		titleBarRect.top, SRCCOPY);
    }

     

    Скобки для одиночных выражений пожалуй я не буду ставить, так как с ними мне лично тяжелее. Приходится более двигать глазами. Плюс я пишу верхнюю и нижнюю скобки каждую в своей строке.

     

    1 час назад, Xipho сказал:

    Не видна. Большое количество вложений вызывает ухудшение читаемости и, соответственно, понимание кода. А вот если ты это растаскиваешь по методам c с говорящими именами - это куда лучше. Ты сразу понимаешь, что происходит в твоем метод, а если становятся интересны детали - проваливаешься в меньший метод. 

     

    Надо посидеть, подумать, прикинуть архитектурно как это разрулить. Можно глянуть с точки зрения паттернов.

    Пока что ничего в голову не приходит. Со временем может.

  14. 2 часа назад, Xipho сказал:

    2. Условие, вложенное в условие, вложенное в цикл, вложенный в условие, вложенное в цикл - мозг не сломался, не? Разбить на маленькие приватные методы для упрощения читаемости.

     

    Спойлер
    int main(void)
    {
    	char* error = "";
    	ALLEGRO_DISPLAY *display = NULL;
    	ALLEGRO_FONT *font = NULL;
    	ALLEGRO_TIMER *timer = NULL;
    	ALLEGRO_EVENT_QUEUE *event_queue = NULL;
    
    	HANDLE mutex = CreateMutex(NULL, FALSE, "Chapter3_CloseMutex-Mutex");
        if (GetLastError() == ERROR_ALREADY_EXISTS) {
    		MessageBoxA(GetForegroundWindow(), "Only one instance of the game can run at a time.", "Chapter3_CloseMutex", MB_OK);
            return 0;
        }
    
    	do
    	{
    		loadHighscore();
    		if(!al_init())
    		{
    			error = "failed to initialize allegro!";
    			break;
    		}
    		if(!al_init_primitives_addon())
    		{
    			error = "failed to initialize primitives!";
    			break;
    		}
    		al_init_font_addon();
    		if(!al_init_ttf_addon())
    		{
    			error = "failed to initialize ttfs!";
    			break;
    		}
    		if(!al_install_keyboard())
    		{
    			error = "failed to initialize keyboard!";
    			break;
    		}
    		if(!(font = al_load_ttf_font("arial.ttf", 18, 0)))
    		{
    			error = "failed to initialize font!";
    			break;
    		}
    		if(!(timer = al_create_timer(1.0 / 20)))
    		{
    			error = "failed to initialize timer!";
    			break;
    		}
    		if(!(display = al_create_display(640, 480)))
    		{
    			error = "failed to initialize display!";
    			break;
    		}
    		if(!(event_queue = al_create_event_queue()))
    		{
    			error = "failed to initialize event_queue!";
    			break;
    		}
    
    		al_register_event_source(event_queue, al_get_display_event_source(display));
    		al_register_event_source(event_queue, al_get_keyboard_event_source());
    		al_register_event_source(event_queue, al_get_timer_event_source(timer));
    
    		al_start_timer(timer);
    
    		while(1)
    		{
    			ALLEGRO_EVENT ev;
    			al_wait_for_event(event_queue, &ev);
    
    			if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
    				break;
    			else if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
    			{
    				if (ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE)
    					break;
    				else if (ev.keyboard.keycode <= 255)
    					keys[ev.keyboard.keycode] = true;
    			}
    			else if (ev.type == ALLEGRO_EVENT_KEY_UP)
    			{
    				if (ev.keyboard.keycode <= 255)
    					keys[ev.keyboard.keycode] = false;
    			}
    			else if (ev.type == ALLEGRO_EVENT_KEY_CHAR)
    				keyboardEvent();
    
    			if (al_is_event_queue_empty(event_queue) && ev.type == ALLEGRO_EVENT_TIMER)
    				drawFrame(font);
    		}
    	} while (0);
    
    	if (mutex)
            CloseHandle(mutex);
    
    	if (strlen(error) > 0)
    	{
    		al_show_native_message_box(NULL, NULL, "error", error, NULL, NULL);
    		return -1;
    	}
    
    	al_destroy_timer(timer);
    	al_destroy_display(display);
    	al_destroy_event_queue(event_queue);
    	saveHighscore();
    
    
    	return 0;
    }

     

    У тебя тоже есть занятные примеры в коде. 😊

    Я помню что ты говорил что это ты писал не для себя, что для себя ты пишешь нормально и в паблик не выкладываешь. Однако все равно, смотрится интересно.

  15. 37 минут назад, Xipho сказал:

    1 - нет фигурных скобок. Ухудшается чтение.

    И правда, меня это тоже напрягало. Ставил по упомянутой тобой возможности.  Считал что так нужно делать обязательно, а делать как говоришь ты, это плохой тон. Теперь буду ставить везде

     

    40 минут назад, Xipho сказал:

    Добавлю: насчет переменных в одном месте - ты точно не путаешь их с полями класса? Потому как поля класса должны быть в одном месте, а вот переменные должны быть рядом с местами, где используются. Основной посыл в том, что код метода должен в идеале читаться как книга - сверху вниз. А как это так получится, если ты все переменные метода соберешь в начале метода? 

    Опять же, прочитав где-то что переменные (не поля класса) нужно объявлять все в одном месте наверху (это я точно помню), я так и делал. Нужно теперь переучивать себя. Думаю это пройдет быстро, так как мне не нравилось собирать их все наверху.

     

    50 минут назад, Xipho сказал:

    2. Условие, вложенное в условие, вложенное в цикл, вложенный в условие, вложенное в цикл - мозг не сломался, не? Разбить на маленькие приватные методы для упрощения читаемости.

    Есть такое. Но здесь не однозначно. Как мне кажется сейчас.

    Ведь так видна сразу вся картина, все перед глазами. А если разбить это на небольшие методы, то для понимания придется часто бегать по ним, и запоминать, как они внутри себя выполняют операции.

     

    Я помню как в Чистом коде читал про эту вложенность и как она вредна. И помню, как я не мог понять как без нее то. Если такая настоит необходимость, что нужно проверить кучу условий.

    1 час назад, Xipho сказал:

    3. Если у тебя везде такой код, не предлагай мне его ревьюить, такой код в нашей платформе я бы ни за что не пропустил.

    У меня весь код такой, и еще хуже. К сожалению.

    Можешь не ревьювить его. Эту кодо-помойку я не вправе кому-то предлагать на рассмотрение. И вообще отнимать у кого-то время принудительно. Так, если просто будет у тебя желание и интерес. Пробежаться быстрым взглядом.

    За советы спасибо. Без советом и опыта других далеко не уедешь, к счастью я это понимаю.

  16. 2 часа назад, Xipho сказал:

    Ёшки-матрёшки! А говорил, что "Чистый код" прочитал...

    Нет обработки исключений? Вроде все как по книге. Смысловые названия переменных. Короткие функции, занимающиеся только рисованием. Нет комментариев. Объявления переменных в одном месте, в начале.

  17. 13 часов назад, roma912 сказал:

    Ну мне пришел в голову такой вариант - написать свой парсер и на его основе уже использовать функции рисования.
    Если хочешь сделать разметку для текста вроде HTML, то придется написать тебе обработчик данной строки.

    HTML версия мне не нравится. В сравнению с методом Append.

    С Append ясно видно какой цвет и для чего установлен. Быстрее ищется глазами, быстрее редактируется. Я остановился на нем.

     

    13 часов назад, roma912 сказал:

    По поводу переноса текста - берешь начальные координаты отрисовки, и высчитываешь от этой координаты вниз некоторое количество пикселей (Допустим шрифт 14 + отступ текста 8 = 22). Примерно такая логика для переноса текста. Ну а далее стандартная отрисовка по координатам.

    Все так. Главное вовремя добавить Y офсет при встрече \n.

    13 часов назад, roma912 сказал:

     (Допустим шрифт 14 + отступ текста 8 = 22).

    Отступ текста 8 = 22 по идее не нужен, если использовать для Y офсета поле LOGFONT lfHeight и функцию ExtTextOutW.

     

    13 часов назад, roma912 сказал:

    Идею для реализации я тебе описал, дальше все за тобой.

    Спасибо за идею.  В интернете тоже советовали что-то подобное. Плюс сам я бывало задумывался на ручным переносом.

     

    Так выглядит разноцветная версия ToolTip'a. Самодельного, на основе простого окна.

    Спойлер

     

    spacer.png

     

     

     

    Это два метода для рисования

    Спойлер
    VOID ToolTip::DrawToolTip()
    	{
    		if (!isThreadLocalMustBeTerminated)
    		{
    			SetLayeredWindowAttributes(hWnd, 0, 1, LWA_ALPHA);
    			GetCursorPos(&cursorPosAbsoluteCurrent);
    			if (hasLinkText)
    				SetWindowPos(hWnd, HWND_TOPMOST, cursorPosAbsoluteCurrent.x + setting.offsetXBetweenCursorAndLinkedTTip,
    					cursorPosAbsoluteCurrent.y + setting.offsetYBetweenCursorAndLinkedTTip, toolTipSize.cx, toolTipSize.cy,
    					SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOOWNERZORDER | SWP_NOZORDER);
    			else
    				SetWindowPos(hWnd, HWND_TOPMOST, cursorPosAbsoluteCurrent.x + setting.offsetXBetweenCursorAndNotLinkedTTip,
    					cursorPosAbsoluteCurrent.y + setting.offsetYBetweenCursorAndNotLinkedTTip, toolTipSize.cx, toolTipSize.cy,
    					SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOOWNERZORDER | SWP_NOZORDER);
    			FillRect(CompatibleDC.compDC, &toolTipClientRect, backgroundBrush);
    			FrameRect(CompatibleDC.compDC, &toolTipClientRect, borderBrush);
    			if (Icon.iconNew)
    				DrawIconEx(CompatibleDC.compDC, iconRect.left, iconRect.top, Icon.iconNew, Icon.IconWidth, Icon.IconHeight, NULL, NULL, DI_NORMAL);
    			if (hasTitleText)
    			{
    				CompatibleDC.ApplyFont(titleFont);
    				dynamicTextYOffset = titleTextRect.top;
    				MoveToEx(CompatibleDC.compDC, titleTextRect.left, titleTextRect.top, NULL);
    				DrawToolTipTexts(titleTextData);
    			}
    			if (hasDescriptionText)
    			{
    				CompatibleDC.ApplyFont(descriptionFont);
    				dynamicTextYOffset = descriptionTextRect.top;
    				MoveToEx(CompatibleDC.compDC, descriptionTextRect.left, descriptionTextRect.top, NULL);
    				DrawToolTipTexts(descriptionTextData);
    			}
    			if (hasLinkText)
    			{
    				CompatibleDC.ApplyFont(linkFont);
    				dynamicTextYOffset = linkTextRect.top;
    				MoveToEx(CompatibleDC.compDC, linkTextRect.left, linkTextRect.top, NULL);
    				DrawToolTipTexts(linkTextData);
    			}
    			BitBlt(CompatibleDC.origDC, 0, 0, toolTipSize.cx, toolTipSize.cy, CompatibleDC.compDC, 0, 0, SRCCOPY);
    		}
    	}
    
    	VOID ToolTip::DrawToolTipTexts(std::vector<ToolTipTextProperties*> toolTipTextComponentTextData)
    	{
    		std::wstring currentString;
    		std::size_t stringsLength = 0;
    		std::wstringstream stringStream;
    		std::size_t textLength = 0;
    		for (auto& textData : toolTipTextComponentTextData)
    		{
    			CompatibleDC.ApplyFontColor(textData->textColor);
    			if (textData->text.find(L'\n') == textData->text.npos)
    				ExtTextOutW(CompatibleDC.compDC, 0, 0, ETO_OPAQUE, NULL, textData->text.c_str(), textData->text.length(), NULL);
    			else
    			{
    				stringsLength = 0;
    				stringStream.clear();
    				stringStream << textData->text;
    				textLength = textData->text.length();
    				while (std::getline(stringStream, currentString, L'\n'))
    				{
    					ExtTextOutW(CompatibleDC.compDC, 0, 0, ETO_OPAQUE, NULL, currentString.c_str(), currentString.length(), NULL);
    					stringsLength += currentString.length();
    					if (stringsLength == textLength - 1)
    					{
    						if (textData->text[textLength - 1] == L'\n')
    						{
    							dynamicTextYOffset += setting.titleTextLogFont.lfHeight;
    							MoveToEx(CompatibleDC.compDC, titleTextRect.left, dynamicTextYOffset, NULL);
    						}
    					}
    					else
    					{
    						if (textData->text[stringsLength] == L'\n')
    						{
    							stringsLength++;
    							dynamicTextYOffset += setting.titleTextLogFont.lfHeight;
    							MoveToEx(CompatibleDC.compDC, titleTextRect.left, dynamicTextYOffset, NULL);
    						}
    					}
    				}
    			}
    		}
    	}

     

    Второй метод паралельно рисованию занимается вертикальным переносом. SetTextAlign установлен заранее в TA_UPDATECP. Для автоматического расчета смещения по Х, для ExtTextOutW.

     

    Если кому-то интересно посмотреть весь код, то проект скоро будет выложен на Github, после небольшого рефаторинга. Чтобы хоть что-то было рабочее.

  18. Привет всем, никто не пробовал реализовать такое?

    Есть желание в ToolTip контроле отображать текст как в Visual Studio. С подсветкой синтаксиса.

    Не для синтаксиса ради, а просто, чтобы была такая возможность.

     

    Вначале была мысль использовать стиль похожий на HTML

    BTN1->ToolTip.DescriptionText = L"<fontColor:#ff0000> January 30, 2011\n </fontColor>"
    									L"<fontColor:#ff0010> Feb 1, 1999\n </fontColor>"
    									L"DesriptionTextExample3 text4";
    									L"<fontColor:#ff0010> March 11, 2014\n </fontColor>";

    , и затем в методе класса разбирать эту строку.

     

    Сейчас есть мысль использовать иной подход

    BTN1->ToolTip.DescriptionText.Append(L"TextOne", RGB(112, 254, 177));
    BTN1->ToolTip.DescriptionText.Append(L"TextTwo"); //Цвет по дефолту.
    BTN1->ToolTip.DescriptionText.Append(L"TextThree \n"); //Цвет по дефолту.
    BTN1->ToolTip.DescriptionText.Append(L"TextFour \n and TextFive", RGB(77, 11, 22));

     

    Затем в цикле вызывать DrawText.

    Вся сложность в расчетах положения следующей строки для DrawText. Так как текст может быть многострочный.

    Думаю создать вектор структур. Структура имеет в себе поле строки, цвета, и положения. Осталось придумать алгоритм расчета положений. Скорее всего он будет основан на DrawText с флагом DT_CALCRECT.

    Как получится рабочий вариант, обязательно напишу.

     

  19. Я правильно понял, что есть число из 8 цифр, и последние четыре цифры - 0001? И это все что известно?

    Если так, то получается что вариантов может быть от 00000001 до 99990001, то есть вариант первый это 00010001, второй это 00020001, всего вариантов 9999. Каждый раз нужно прибавлять по 10 000.

    Что с этими 9999 вариантами делать только.

  20. 5 часов назад, Xipho сказал:

    Тут вариантов, на самом деле, много, как и в любой айтишной задаче. Я бы, наверное, в таком случае сделал бы какой-нибудь менеджер ресурсов, ответственностью которого было бы как раз предоставлять доступ к ресурсам и их потокобезопасное изменение.

     

    В случае с GUI винды уже давно всё придумано - у главного потока есть цикл обработки сообщений, в него и приходят сообщения от разных контролов. И есть глобальная очередь системных сообщений.

     

    Смотри, это нормально, когда классы внутри твоей библиотеки свободно общаются между собой. Но не забывай про инкапсуляцию - клиенты, то есть, те, кто будут пользоваться библиотекой, не должны иметь доступ в ее кишки. Наружу должны торчать только внятные API. Например, клиент хочет изменить размеры окна - ему неважно, как это делается внутри средствами системы или библиотеки - ему важно, чтобы у него был способ, то есть, какой-то метод, который можно вызвать, и библиотека сделает нужную работу. Исходя из вышесказанного - у тебя классы могут быть френдами внутри библиотеки, но то, что ты выставляешь на показ (для использования снаружи), должно быть четко регламентировано и иметь свои границы доступа. Если не понятно, скажи, постараюсь объяснить это на каком-нибудь конкретном примере.

    Мне понятно это, и к счастью сейчас все именно так.

    Библиотека строится на трех уровнях. Первый уровень самый простой, использовать уже заранее определенные внешние виды контролов. Например есть полностью самодельный TitleBar окна. Или полностью самодельное Menu, или PopuoMenu. На первом уровне пользователь просто вызывает их свойства. Enabled или Disabled, Checked или Unchecked. На втором уровне в конструкторе класса в самом начале вынесены переменные-поля которые задают цвет того-то или того-то, отступы, и прочее. Изменение их не влечет ничего серьезного для кода. И третий уровень, когда пользователь может сам изменить что-то в логике кода контрола.

    5 часов назад, Xipho сказал:

    Есть и желание, и интерес, но не уверен, что смогу выделить на это много времени. В любом случае, постепенно отсматривать, конечно же, буду, и, если хочешь, будем проводить что-то вроде код ревью. Мне это поможет получше вспомнить плюсы, а то я давно на них не кодил, а тебе, возможно, мои советы по конкретному коду тоже будут полезны.

     

    Отлично, как будет готово, я напишу.

  21. 3 часа назад, Xipho сказал:

    Ты путаешь теплое с мягким. Акторы - это не дизайн паттерн. Это модель программирования.

    Да, я думал что это паттерн.

     

    3 часа назад, Xipho сказал:

    Но если у тебя суть много поточности состоит именно в том, чтобы передавать/получать состояния между основным потоком и остальными - тебе за глаза хватит семафоров, мьютексов, и, на худой конец, очередей.

    Я хотел использовать критические секции, так как читал что они быстрее. Нет переключения на ядро. Хотя они процессозависимые.

     

    3 часа назад, Xipho сказал:

    Хэндл какого-то окна, как правило, вещь иммьютабельная, потому его спокойно можно хранить где-то в общем хранилище объектов, к которому есть доступ только на чтение у всех потоков, кроме главного (окошки порождаются в главном потоке, и их надо заносить в реестр).

    Я про хенл иконки, HICON. Который хранится у меня в классе Icon16. Например, сторонний поток читает HICON, и в это же время, главный GUI поток по какой-то причине его изменяет. В таком случае Актор не подойдет, и нужен будет синхронизатор на доступ, - это я не совсем понял про акторов, из того видео, хотя что то такое там говорилось.

    Пока понимаю что Актор полезен только при Fire-and-forget схеме. В остальных, например нужно получить из стороннего потока ресурс GUI, здесь и сейчас, нужны будут синхронизаторы.

     

    3 часа назад, Xipho сказал:

    И еще, мне кажется, тебя захлестнуло волной новых знаний, и ты начинаешь оверинжинирить там, где это совершенно ни к чему.

    Возможно. Хотя я и сам хочу как проще. Я пытался заранее разобраться с многопоточностью. Чтобы не пришлось потом снова все переписывать.

     

    3 часа назад, Xipho сказал:

    И не забывай, что в идеале программировать надо интерфейсами, чтобы потом можно было без проблем запиливать столько реализаций, сколько понадобится.

    Примерное представление как должна быть построена библиотека у меня уже есть. Хотя и сомнения есть. Например из-за частого использование friend class. Но без него, мне кажется, придется писать кучу геттеров и сеттеров.

    Я узнавал уже про правильность использование friend class, кто-то за, кто-то против. Я пока с теми кто за.

     

    3 часа назад, Xipho сказал:

    Скачал себе, заметил в ней как-раз что-то про GUI и контроллы. Спасибо.

     

    Я удалил Docking систему, пока-что, из библиотеки. И еще некоторое. Можно сказать что я вообще начал писать снова все с ноля.

    Хочу закончить основные свойства контролов, и самодельное меню. И пока так выложит на гитхаб. Чтобы хоть что-то было рабочее.

    Если будет желание и интерес, посмотри пожалуйста. Вместе посмеемся. Хотя я и пишу это не для серьезного чего, а так, для того чтобы занять себя.

     

  22. 11 часов назад, Xipho сказал:

    Оставлю еще раз тут линк на всякий случай https://www.oreilly.com/library/view/head-first-design/0596007124/

    О, у них обновленная версия появилась, надо будет глянуть https://www.oreilly.com/library/view/head-first-design/9781492077992/

    Скачал книгу 2022 года, жалко что в ней нет про модель Акторов. Я вчера читал про эту модель и смотрел видео, но немного не понял суть. Я слышал что она используется при многопоточности.

    Акторы, если они представлены потоками, общаются только через сообщения, типа Fire-and-forget. Отправил и забыл. Это понятно и просто. Например, поток кодирует видео, и каждую секунду отправляет GUI потоку сообщение об обновлении прогрессбара.

    Но что если стороннему потоку понадобится например хендл иконки какого-то окна, и понадобится он ему здесь и сейчас. Тогда Fire-and-forget уже не будет работать, и нужно будет реализовывать для таких случаев Критические секции или им подобные блокировки. Или использовать событие в стороннем потоке, и ждать пока поток GUI не обработает сообщение запроса и не вернет каким-либо образом нужный хендл.

    Вот этот второй момент, с запросом, не очень ясен.

  23. 1 час назад, Xipho сказал:

    Не так. Избыточные комментарии есть признак грязного кода. Потому что методы и их параметры должны иметь говорящие названия. А вот комментарии с общими назначениями классов и рекомендациями по их использованию - это правильные комментарии. И про это в книге тоже сказано (по крайней мере, в английской версии, в русской - хз, не читал).

    Я тоже английскую версию читал. Я с некоторого времени вообще не могу читать русские.

    На днях смотрел про "1C", про встроенный там язык

    Перем количествоЦветов;
    Перем разрешениеЭкрана;
    
    Если количествоЦветов < 256 Тогда
    разрешениеЭкрана = 1024;
    Иначе
    разрешениеЭкрана = 1920;
    КонецЕсли;

    А веди те для кого английский родной так и видят свой код. Но для меня это тяжело.

    Про комментарии я помню он писал что да, в некоторых случаях они полезны, но я воспринял это как лучше их совсем не писать. Лучше да, чтобы сам код за себя говорил.

     

     

  24. В 24.03.2022 в 11:17, Xipho сказал:

    Вдобавок к паттернам обязательно читать дядюшку Боба (Роберт Мартин) "Чистый код" (примеры на Java, но суть уловить несложно), и его же "Чистая архитектура" (книга более-менее абстрактная без привязок к коду). 

    Прямо настоятельно рекомендую отложить код в сторону до полного прочтения книг, которые предлагал выше и предлагаю сейчас.

    Почитал "Чистый код", почитал про паттерны. Заново переписываю уже написанное.

    Особенно понравилось про комментарии, - я так ленился их писать. А согласно книге, они есть признак грязного кода.

     

    В 08.04.2022 в 11:36, Xipho сказал:

    Опасный подход. Если вдруг понадобится использовать один класс в параллель (несколько потоков), словишь очень неприятные побочные эффекты.

     

    Кстати. А как ты решил проблему использования твоего графического фреймворка в многопоточных приложениях?

    В случаях, если, например, кто-то будет использовать твой фреймворк для построения графического интерфейса приложения, занимающегося конвертацией видео.

    Ведь по ходу конвертации тебе надо будет давать возможность пользователю обновлять графический интерфейс, для отображения, например, прогресса конвертации.

    Решена ли в твоих классах проблема состояния гонки (race condition)?

    Недавно прочитал Рихтера, и хотел было реализовывать многопоточность для GUI, но, сегодня наткнулся на этот материал. Согласно ему, лучше использовать только один поток для управления всем GUI. Что не может не радовать. Для других потоков, которые буду использоваться для серьезных расчетов, буду использовать PostThreadMessageW, или другие подобные, для отправки сообщения контролу в главные GUI поток.

    Надеюсь что план правильный.

  25. В 04.07.2022 в 14:47, DragonForce сказал:

    Есть ли возможность в Auto assemble вызывать WINAPI - RegDeleteKeyW?

    То-есть сделать что-то типа

    push *Первый параметр*

    push *Второй параметр*

    call RegDeleteKeyW

    Я когда-то делал что-то подобное для игры Dirt 5. В определенный момент загрузки карты в определенной игровой функции мне нужно было считать бинарный файл в память игры.

     

    Нужно чтобы в игре была загружена библиотека, в которой находится нужная функция, иначе прежде придется загрузить еще и DLL для нее.

    Для понимания как и какие параметры передавать, я писал на С++ образец WinAPI ReadFile, компилировал, запускал, подключал CE. Далее находил вызов, сверял с соглашением x64 calling convention, и уже после пробовал написать подобное для игры.

    После возврата из некоторых WinAPI функций могут изменяться XMM регистры не входящие в состав параметров.  Для этого до вызова функции нужно сохранить их значения отдельно в памяти, потом после вызова восстановить.

     

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.