Blog. Just Blog

Красивый градиентный ProgressBar на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Элемент ProgressBar, он же индикатор прогресса, используется в тех случаях, когда нужно показать пользователю степень завершения какого-либо действия. Есть стандартный элемент msctls_progress32, но он практически не поддается настройке в плане дизайна. Например, его нельзя сделать градиентным, чтобы заливка плавно перетекала от зеленого, через желтый и до красного. Но можно нарисовать красивую полосу ProgressBar самостоятельно. Идею я позаимствовал у kero, знаю, что он сюда иногда заглядывает и надеюсь, что он будет не против. Оригинал написан на MASM, я доработал его и портировал на FASM.

Принцип создания градиентного ProgressBar
Принцип создания градиентного ProgressBar

Принцип создания градиентного прогрессбара заключается в том, что на элемент STATIC на всю его площадь накладывается готовое изображение. Затем создается дополнительное непрозрачное окно, являющееся дочерним к STATIC и расположенное поверх него. Регулируя размер и положение этого дочернего окна, открывается та или иная площадь изображения, пропорционально нужному проценту заполнения. Это чем-то напоминает создание рейтингов на CSS. Еще одна хитрость заключается в том, что изображение прогрессбара загружается из ресурсов функцией LoadImage, которая позволяет сразу указывать нужные размеры. Если исходное изображение меньше, то оно автоматически растягивается. Таким образом, для создания цветного прогрессбара достаточно однопиксельного изображения, а из крохотного bitmap'а шириной всего в три пиксела получается красивый градиентный прогрессбар любого размера.

Для того, чтобы в своем приложении было удобно работать с такими самодельными элементами интерфейса, я оформил все в виде отдельной процедуры. Никаких дополнительных переменных объявлять не надо, все что нужно, хранится в пользовательских данных самого STATIC. Ну и картинки в ресурсах, конечно.
  1. ;----------------------------------------------------------------------
  2. ; Процедура отрисовки градиентного ProgressBar
  3. ;----------------------------------------------------------------------
  4. ; Автор: ManHunter / PCL (http://www.manhunter.ru)
  5. ; на основе кода kero (http://www.geocities.ws/xmemor)
  6. ;----------------------------------------------------------------------
  7. ; Параметры:
  8. ;   hWnd - хэндл STATIC, на котором будет отрисован ProgressBar
  9. ;   dProc - процент заполнения (0..100)
  10. ;----------------------------------------------------------------------
  11. proc ProgressBar hWnd:DWORD, dProc:DWORD
  12.         locals
  13.                 wXY    RECT
  14.                 width  dd ?
  15.                 height dd ?
  16.         endl
  17.  
  18.         ; Индексы изображений в ресурсах
  19.         INDEX_HORIZONTAL_BITMAP = 1
  20.         INDEX_VERTICAL_BITMAP   = 2
  21.  
  22.         pusha
  23.         ; Получить размеры STATIC для отрисовки ProgressBar
  24.         lea     ebx,[wXY]
  25.         invoke  GetClientRect,[hWnd],ebx
  26.         mov     eax,[ebx+RECT.right]
  27.         sub     eax,[ebx+RECT.left]
  28.         mov     [width],eax
  29.         mov     eax,[ebx+RECT.bottom]
  30.         sub     eax,[ebx+RECT.top]
  31.         mov     [height],eax
  32.  
  33.         ; Слайдер уже есть?
  34.         invoke  GetWindowLong,[hWnd],GWL_USERDATA
  35.         or      eax,eax
  36.         jnz     .set_slider
  37.  
  38.         ; Загрузить изображение и растянуть его до нужных размеров
  39.         invoke  ShowWindow,[hWnd],SW_HIDE
  40.         invoke  GetModuleHandle,0
  41.  
  42.         ; Выбрать тип изображения для загрузки
  43.         mov     ebx,[width]
  44.         cmp     ebx,[height]
  45.         jb      .load_vertical_image
  46.  
  47. .load_horizontal_image:
  48.         invoke  LoadImage,eax,INDEX_HORIZONTAL_BITMAP,\
  49.                 IMAGE_BITMAP,[width],[height],LR_DEFAULTCOLOR
  50.         jmp     @f
  51.  
  52. .load_vertical_image:
  53.         invoke  LoadImage,eax,INDEX_VERTICAL_BITMAP,IMAGE_BITMAP,\
  54.                 [width],[height],LR_DEFAULTCOLOR
  55. @@:
  56.         ; Установить изображение на STATIC
  57.         invoke  SendMessage,[hWnd],STM_SETIMAGE,IMAGE_BITMAP,eax
  58.  
  59.         ; Создать дочернее окно слайдера
  60.         invoke  GetModuleHandle,0
  61.         invoke  CreateWindowEx,0,.class,0,WS_CHILD+WS_VISIBLE,\
  62.                 0,0,[width],[height],[hWnd],0,eax,0
  63. .set_slider:
  64.         ; Хэндл слайдера
  65.         mov     ebx,eax
  66.  
  67.         ; Сохранить или обновить хэнд слайдера
  68.         invoke  SetWindowLong,[hWnd],GWL_USERDATA,eax
  69.  
  70.         ; Вычислить размер слайдера
  71.         mov     eax,[width]
  72.         cmp     eax,[height]
  73.         jb      .set_vertical_slider
  74.  
  75. .set_horizontal_slider:
  76.         mov     eax,[dProc]
  77.         cmp     eax,100
  78.         jbe     @f
  79.         mov     eax,100
  80. @@:
  81.         xor     edx,edx
  82.         mov     ecx,[width]
  83.         imul    ecx
  84.         mov     ecx,100
  85.         div     ecx
  86.  
  87.         ; Передвинуть слайдер на прогресс-баре
  88.         invoke  MoveWindow,ebx,eax,0,[width],[height],TRUE
  89.         jmp     .show_slider
  90.  
  91. .set_vertical_slider:
  92.         mov     eax,[dProc]
  93.         cmp     eax,100
  94.         jbe     @f
  95.         mov     eax,100
  96. @@:
  97.         xor     edx,edx
  98.         mov     ecx,[height]
  99.         imul    ecx
  100.         mov     ecx,100
  101.         div     ecx
  102.  
  103.         ; Передвинуть слайдер на прогресс-баре
  104.         mov     ecx,[height]
  105.         sub     ecx,eax
  106.         invoke  MoveWindow,ebx,0,0,[width],ecx,TRUE
  107.  
  108. .show_slider:
  109.         invoke  ShowWindow,[hWnd],SW_SHOW
  110.  
  111.         popa
  112.         ret
  113.  
  114. .class  db 'STATIC',0
  115.  
  116. endp
Процедура принимает два параметра: hWnd - хэндл окна STATIC, на котором будет отрисован ProgressBar, dProc - процент заполнения (0..100). Корректность хэедла не проверяется, ответственность за входящие данные лежит на вас. В ресурсы надо добавить два изображения в формате BMP, которые будут использоваться для заполнения горизонтального и вертикального прогрессбаров. При необходимости измените константы индексов bitmap, если они конфликтуют с уже используемыми в вашей программе.
  1. section '.rsrc' resource data readable
  2.  
  3.   resource bitmaps,\
  4.            INDEX_HORIZONTAL_BITMAP,LANG_NEUTRAL,progress1,\
  5.            INDEX_VERTICAL_BITMAP,LANG_NEUTRAL,progress2
  6.  
  7.   bitmap progress1,'progress_h.bmp'
  8.   bitmap progress2,'progress_v.bmp'
Важно, чтобы для используемого элемента STATIC в ресурсах сразу были прописаны размеры. Полоса прогрессбара будет автоматически растянута на всю его площадь. Окно слайдера специально нигде описывать не надо, оно будет динамически создано при установке первого значения прогрессбара. В основном коде работа с прогрессбаром выполняется примерно так:
  1.         ; Получить хэндл STATIC
  2.         invoke  GetDlgItem,[hwnddlg],ID_PROGRESSBAR
  3.         ; Установить прогрессбар на позицию percent
  4.         stdcall ProgressBar,eax,[percent]
Если вертикальный прогрессбар в вашей программе не используется, то код можно заметно сократить. Из ресурсов, соответственно, убирается вертикальный bitmap. Параметры вызова точно такие же, как и для полного варианта процедуры.
  1. ;----------------------------------------------------------------------
  2. ; Процедура отрисовки горизонтального градиентного ProgressBar
  3. ;----------------------------------------------------------------------
  4. ; Автор: ManHunter / PCL (http://www.manhunter.ru)
  5. ; на основе кода kero (http://www.geocities.ws/xmemor)
  6. ;----------------------------------------------------------------------
  7. ; Параметры:
  8. ;   hWnd - хэндл STATIC, на котором будет отрисован ProgressBar
  9. ;   dProc - процент заполнения (0..100)
  10. ;----------------------------------------------------------------------
  11. proc ProgressBar hWnd:DWORD, dProc:DWORD
  12.         locals
  13.                 wXY    RECT
  14.                 width  dd ?
  15.                 height dd ?
  16.         endl
  17.  
  18.         ; Индекс изображения в ресурсах
  19.         INDEX_HORIZONTAL_BITMAP = 1
  20.  
  21.         pusha
  22.         ; Получить размеры STATIC для отрисовки ProgressBar
  23.         lea     ebx,[wXY]
  24.         invoke  GetClientRect,[hWnd],ebx
  25.         mov     eax,[ebx+RECT.right]
  26.         sub     eax,[ebx+RECT.left]
  27.         mov     [width],eax
  28.         mov     eax,[ebx+RECT.bottom]
  29.         sub     eax,[ebx+RECT.top]
  30.         mov     [height],eax
  31.  
  32.         ; Слайдер уже есть?
  33.         invoke  GetWindowLong,[hWnd],GWL_USERDATA
  34.         or      eax,eax
  35.         jnz     .set_slider
  36.  
  37.         ; Загрузить изображение и растянуть его до нужных размеров
  38.         invoke  ShowWindow,[hWnd],SW_HIDE
  39.         invoke  GetModuleHandle,0
  40.  
  41.         invoke  LoadImage,eax,INDEX_HORIZONTAL_BITMAP,\
  42.                 IMAGE_BITMAP,[width],[height],LR_DEFAULTCOLOR
  43.         ; Установить изображение на STATIC
  44.         invoke  SendMessage,[hWnd],STM_SETIMAGE,IMAGE_BITMAP,eax
  45.  
  46.         ; Создать дочернее окно слайдера
  47.         invoke  GetModuleHandle,0
  48.         invoke  CreateWindowEx,0,.class,0,WS_CHILD+WS_VISIBLE,\
  49.                 0,0,[width],[height],[hWnd],0,eax,0
  50. .set_slider:
  51.         ; Хэндл слайдера
  52.         mov     ebx,eax
  53.  
  54.         ; Сохранить или обновить хэнд слайдера
  55.         invoke  SetWindowLong,[hWnd],GWL_USERDATA,eax
  56.  
  57.         ; Вычислить размер слайдера
  58.         mov     eax,[dProc]
  59.         cmp     eax,100
  60.         jbe     @f
  61.         mov     eax,100
  62. @@:
  63.         xor     edx,edx
  64.         mov     ecx,[width]
  65.         imul    ecx
  66.         mov     ecx,100
  67.         div     ecx
  68.  
  69.         ; Передвинуть слайдер на прогресс-баре
  70.         invoke  MoveWindow,ebx,eax,0,[width],[height],TRUE
  71.         invoke  ShowWindow,[hWnd],SW_SHOW
  72.  
  73.         popa
  74.         ret
  75.  
  76. .class  db 'STATIC',0
  77.  
  78. endp
В приложении примеры двух программ с исходными текстами, одна из них реализует оба типа прогрессбаров - вертикальный и горизонтальный, вторая предназначена только для работы с горизонтальным прогрессбаром.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

ProgressBar.Demo.zip (10,484 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 3047 | Комментариев: 5

Метки: Assembler, окна
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (25.07.2016 в 17:54):
А это уже как-нибудь сам.
Жека (25.07.2016 в 17:48):
стоп, стоп, стоп.
А серую полоску Child?
ManHunter (25.07.2016 в 14:21):
Нигде, это фотошопная картинка для демонстрации принципа формирования прогрессбара. Но такой градиент без проблем растягивается из трехпиксельного битмапа, только цвета надо подобрать.

Добавил в архив персональный прогрессбар.
Жека (25.07.2016 в 13:59):
а где пример со скрина? Тот прогрессбар ещё интереснее
prostovova (24.07.2016 в 15:21):
Красиво.
Есть еще (GRush Controls) homm86.narod.ru, посмотреть бы пример на FASM.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0068 сек.) / Память: 4.5 Mb
Наверх