PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/ClanLib-2.3.6/Sources/GUI/Components/Menus/popupmenu_window.cpp

#
C++ | 411 lines | 298 code | 73 blank | 40 comment | 37 complexity | 0095459a1108d34fa4c02ef1b3e94cfb MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. /*
  2. ** ClanLib SDK
  3. ** Copyright (c) 1997-2011 The ClanLib Team
  4. **
  5. ** This software is provided 'as-is', without any express or implied
  6. ** warranty. In no event will the authors be held liable for any damages
  7. ** arising from the use of this software.
  8. **
  9. ** Permission is granted to anyone to use this software for any purpose,
  10. ** including commercial applications, and to alter it and redistribute it
  11. ** freely, subject to the following restrictions:
  12. **
  13. ** 1. The origin of this software must not be misrepresented; you must not
  14. ** claim that you wrote the original software. If you use this software
  15. ** in a product, an acknowledgment in the product documentation would be
  16. ** appreciated but is not required.
  17. ** 2. Altered source versions must be plainly marked as such, and must not be
  18. ** misrepresented as being the original software.
  19. ** 3. This notice may not be removed or altered from any source distribution.
  20. **
  21. ** Note: Some of the libraries ClanLib may link to may have additional
  22. ** requirements or restrictions.
  23. **
  24. ** File Author(s):
  25. **
  26. ** Harry Storbacka
  27. ** Magnus Norddahl
  28. */
  29. #include "GUI/precomp.h"
  30. #include "popupmenu_window.h"
  31. #include "API/GUI/gui_manager.h"
  32. #include "API/GUI/gui_message.h"
  33. #include "API/GUI/gui_component_description.h"
  34. #include "API/GUI/gui_message_close.h"
  35. #include "API/GUI/gui_message_input.h"
  36. #include "API/GUI/gui_message_pointer.h"
  37. #include "API/GUI/Components/popupmenu.h"
  38. #include "API/Display/Window/input_event.h"
  39. #include "API/Display/Render/graphic_context.h"
  40. #include "API/Display/Font/font.h"
  41. #include "API/Display/2D/draw.h"
  42. #include "API/Display/Render/texture.h"
  43. #include "API/Display/Window/keys.h"
  44. #include "popupmenu_impl.h"
  45. #include "../../gui_css_strings.h"
  46. CL_GUITopLevelDescription CL_PopupMenuWindow::create_toplevel_description()
  47. {
  48. CL_GUITopLevelDescription window_desc;
  49. window_desc.set_allow_resize(false);
  50. window_desc.set_topmost(true);
  51. window_desc.show_caption(false);
  52. window_desc.set_tool_window(true);
  53. window_desc.set_visible(false);
  54. window_desc.set_drop_shadow(true);
  55. window_desc.set_using_gui_window_cache(true);
  56. window_desc.set_position(CL_Rect(0,0,32,32), false);
  57. return window_desc;
  58. }
  59. //////////////////////////////////////////////////////////////////////////
  60. // Construction
  61. CL_PopupMenuWindow::CL_PopupMenuWindow(const CL_PopupMenu &menu, const CL_Point &screen_position, CL_GUIComponent *owner)
  62. : CL_GUIComponent(owner, create_toplevel_description()),
  63. menu(menu), selected(-1)
  64. {
  65. set_type_name(CssStr::PopupMenuWindow::type_name);
  66. set_class_name(menu.get_class_name());
  67. prop_icon_column_width = CL_GUIThemePartProperty("icon-column-width", "30");
  68. create_parts();
  69. CL_Rect rect_pos(screen_position, calc_desired_size());
  70. set_window_geometry(rect_pos);
  71. set_visible(true, false);
  72. func_close().set(this, &CL_PopupMenuWindow::on_close);
  73. }
  74. CL_PopupMenuWindow::~CL_PopupMenuWindow()
  75. {
  76. }
  77. //////////////////////////////////////////////////////////////////////////
  78. // Attributes
  79. CL_PopupMenuItem CL_PopupMenuWindow::get_selected_item()
  80. {
  81. return menu.get_item_at(selected);
  82. }
  83. int CL_PopupMenuWindow::get_selected_item_index()
  84. {
  85. return selected;
  86. }
  87. CL_Size CL_PopupMenuWindow::get_preferred_size() const
  88. {
  89. return part_component.get_preferred_size();
  90. }
  91. CL_Point CL_PopupMenuWindow::get_submenu_screen_position()
  92. {
  93. CL_Rect sel_item_rect = get_item_rect(selected);
  94. return component_to_screen_coords(sel_item_rect.get_top_right());
  95. }
  96. //////////////////////////////////////////////////////////////////////////
  97. // Operations
  98. void CL_PopupMenuWindow::select_next()
  99. {
  100. selected++;
  101. if (selected < menu.get_item_count()-1)
  102. {
  103. CL_PopupMenuItem pmi = menu.get_item_at(selected);
  104. if (pmi.is_separator() || pmi.is_disabled())
  105. selected++;
  106. }
  107. if (selected >= menu.get_item_count())
  108. selected = 0;
  109. request_repaint();
  110. }
  111. void CL_PopupMenuWindow::select_previous()
  112. {
  113. selected--;
  114. if (selected > 0)
  115. {
  116. CL_PopupMenuItem pmi = menu.get_item_at(selected);
  117. if (pmi.is_separator() || pmi.is_disabled())
  118. selected--;
  119. }
  120. if (selected < 0)
  121. selected = menu.get_item_count()-1;
  122. request_repaint();
  123. }
  124. void CL_PopupMenuWindow::do_selected_item_special_action()
  125. {
  126. CL_PopupMenuItem pmi = menu.get_item_at(selected);
  127. if (pmi.is_null() || pmi.is_disabled() || pmi.is_separator() || pmi.has_submenu())
  128. return;
  129. if (pmi.is_checkable())
  130. {
  131. pmi.set_checked(!pmi.is_checked());
  132. }
  133. }
  134. void CL_PopupMenuWindow::select_item_at(CL_Point mouse_pos)
  135. {
  136. for (int i = 0; i < menu.get_item_count(); i++)
  137. {
  138. if (menu.get_item_at(i).is_separator())
  139. i++;
  140. CL_Rect item_rect = get_item_rect(i);
  141. if (item_rect.contains(mouse_pos))
  142. {
  143. selected = i;
  144. request_repaint();
  145. return;
  146. }
  147. }
  148. }
  149. void CL_PopupMenuWindow::set_item_pressed(bool pressed)
  150. {
  151. item_pressed = true;
  152. }
  153. //////////////////////////////////////////////////////////////////////////
  154. // Implementation
  155. bool CL_PopupMenuWindow::on_close()
  156. {
  157. if(!menu.func_close().is_null())
  158. menu.func_close().invoke();
  159. return true;
  160. }
  161. void CL_PopupMenuWindow::on_render(CL_GraphicContext &gc, const CL_Rect &update_rect)
  162. {
  163. CL_Rect rect = get_geometry().get_size();
  164. part_component.render_box(gc, rect, update_rect);
  165. if (menu.impl->joiner_width > 0)
  166. {
  167. CL_Rect joiner_rect(0, 0, menu.impl->joiner_width, part_menubar_joiner.get_preferred_height());
  168. part_menubar_joiner.render_box(gc, joiner_rect, update_rect);
  169. }
  170. CL_Rect client_box = part_component.get_content_box(rect);
  171. int offset = 0;
  172. int count = menu.get_item_count();
  173. for (int index = 0; index < count; index++)
  174. {
  175. CL_PopupMenuItem item = menu.get_item_at(index);
  176. bool is_selected = (index == selected);
  177. int row_height = 0;
  178. if (item.is_separator())
  179. {
  180. row_height = part_separator.get_preferred_height();
  181. CL_Rect separator_render_rect(client_box.left, offset, client_box.right, offset+row_height);
  182. CL_Rect separator_content_rect = part_separator.get_content_box(separator_render_rect);
  183. separator_content_rect.right -= 4; // This thing is already a hack (render to content to render content as render, wtf? :))
  184. separator_content_rect.top += 3; // More hacks..
  185. separator_content_rect.bottom += 3; // Something is really wrong about this stuff. But it fixes the visual layout.
  186. part_separator.render_box(gc, separator_content_rect, update_rect);
  187. }
  188. else
  189. {
  190. part_item_row.set_state(CssStr::selected, is_selected);
  191. part_item_icon.set_state(CssStr::selected, is_selected);
  192. part_item_label.set_state(CssStr::selected, is_selected);
  193. part_item_row.set_state(CssStr::disabled, item.is_disabled());
  194. part_item_icon.set_state(CssStr::disabled, item.is_disabled());
  195. part_item_check.set_state(CssStr::disabled, item.is_disabled());
  196. part_item_label.set_state(CssStr::disabled, item.is_disabled());
  197. part_item_accel_label.set_state(CssStr::disabled, item.is_disabled());
  198. row_height = part_item_row.get_preferred_height();
  199. // row rect
  200. CL_Rect row_rect(CL_Point(client_box.left, client_box.top + offset), CL_Size(client_box.right, row_height));
  201. part_item_row.render_box(gc, row_rect, update_rect);
  202. CL_Rect row_box = part_item_row.get_content_box(row_rect);
  203. // icon or check
  204. if (item.is_checkable())
  205. {
  206. if (item.is_checked())
  207. {
  208. CL_Rect rect(CL_Point(row_box.left, (row_box.top + row_box.bottom)/2 - check_size.height/2), check_size);
  209. part_item_check.render_box(gc, rect, update_rect);
  210. }
  211. }
  212. else
  213. {
  214. CL_PixelBuffer pbuf_icon = item.get_icon();
  215. if (!pbuf_icon.is_null())
  216. {
  217. CL_Size icon_size = pbuf_icon.get_size();
  218. CL_Rect rect(CL_Point(row_box.left, (row_box.top + row_box.bottom)/2 - icon_size.height/2), icon_size);
  219. CL_Texture tex(gc, pbuf_icon.get_size());
  220. tex.set_min_filter(cl_filter_nearest);
  221. tex.set_mag_filter(cl_filter_nearest);
  222. tex.set_image(pbuf_icon);
  223. CL_Colorf color = CL_Colorf::white;
  224. if (item.is_disabled())
  225. color.a = 0.25f;
  226. gc.set_texture(0, tex);
  227. CL_Draw::texture(gc, rect, color);
  228. }
  229. }
  230. // item text
  231. CL_Size text_size = part_item_label.get_text_size(gc, item.get_text());
  232. CL_Size text_full_size = part_item_label.get_render_box(text_size).get_size();
  233. CL_Rect label_render_rect(row_box.left + icon_column_width, row_box.top, row_box.left + icon_column_width + text_full_size.width, row_box.bottom);
  234. CL_Rect label_content_rect = part_item_label.get_content_box(label_render_rect);
  235. part_item_label.render_box(gc, label_render_rect, update_rect);
  236. part_item_label.render_text(gc, item.get_text(), label_content_rect, update_rect);
  237. int center_y = row_box.get_center().y;
  238. int arrow_width = part_submenu_arrow.get_preferred_width();
  239. int arrow_height = part_submenu_arrow.get_preferred_height();
  240. if (item.has_submenu())
  241. {
  242. CL_Rect arrow_rect(
  243. row_box.right - arrow_width,
  244. center_y - arrow_height/2,
  245. row_box.right,
  246. center_y + arrow_height/2);
  247. CL_Rect arrow_content = part_submenu_arrow.get_content_box(arrow_rect);
  248. part_submenu_arrow.render_box(gc, arrow_content, update_rect);
  249. }
  250. else if (!item.get_accelerator_text().empty())
  251. {
  252. // accelerator text
  253. CL_Size accel_text_size = part_item_accel_label.get_text_size(gc, item.get_accelerator_text());
  254. CL_Size accel_text_full_size = part_item_accel_label.get_render_box(accel_text_size).get_size();
  255. CL_Rect accel_render_rect(
  256. row_box.right-arrow_width-accel_text_full_size.width,
  257. label_content_rect.top,
  258. row_box.right-arrow_width,
  259. label_content_rect.bottom);
  260. CL_Rect accel_content_rect = part_item_accel_label.get_content_box(accel_render_rect);
  261. part_item_accel_label.render_text( gc, item.get_accelerator_text(), accel_content_rect, update_rect);
  262. }
  263. }
  264. offset += row_height;
  265. }
  266. }
  267. CL_Size CL_PopupMenuWindow::calc_desired_size()
  268. {
  269. CL_GraphicContext &gc = get_gc();
  270. CL_Size size(0,0);
  271. int count = menu.get_item_count();
  272. for (int index = 0; index < count; index++)
  273. {
  274. CL_PopupMenuItem item = menu.get_item_at(index);
  275. bool is_selected = (index == selected);
  276. part_item_row.set_state(CssStr::selected, is_selected);
  277. part_item_icon.set_state(CssStr::selected, is_selected);
  278. part_item_label.set_state(CssStr::selected, is_selected);
  279. int row_height = 0;
  280. if (item.is_separator())
  281. row_height = part_separator.get_preferred_height();
  282. else
  283. row_height = part_item_row.get_preferred_height();
  284. int icon_width = part_item_icon.get_preferred_width();
  285. CL_Size text_size = part_item_label.get_text_size(gc, item.get_text());
  286. CL_Size text_full_size = part_item_label.get_render_box(text_size).get_size();
  287. CL_Size accel_text_size = part_item_accel_label.get_text_size(gc, item.get_accelerator_text());
  288. CL_Size accel_text_full_size = part_item_accel_label.get_render_box(accel_text_size).get_size();
  289. int arrow_width = part_submenu_arrow.get_preferred_width();
  290. int w = icon_width + text_full_size.width + accel_text_full_size.width + arrow_width;
  291. if (size.width < w)
  292. size.width = w;
  293. size.height += row_height;
  294. }
  295. size = part_component.get_render_box(size).get_size();
  296. if (size.width < menu.get_minimum_width())
  297. {
  298. size.width = menu.get_minimum_width();
  299. }
  300. return size;
  301. }
  302. CL_Rect CL_PopupMenuWindow::get_item_rect(int index)
  303. {
  304. int count = menu.get_item_count();
  305. int row_height = part_item_row.get_preferred_height();
  306. int separator_height = part_separator.get_preferred_height();
  307. CL_PopupMenuItem item = menu.get_item_at(index);
  308. CL_Rect content_area = part_component.get_content_box(get_geometry().get_size());
  309. int w = content_area.get_width();
  310. int y = content_area.top;
  311. for (int i = 0; i < count; i++)
  312. {
  313. if (i == index)
  314. break;
  315. if (menu.get_item_at(i).is_separator())
  316. y += separator_height;
  317. else
  318. y += row_height;
  319. }
  320. CL_Rect rect(0, y, w, y+row_height);
  321. return rect;
  322. }
  323. void CL_PopupMenuWindow::create_parts()
  324. {
  325. part_component = CL_GUIThemePart(this);
  326. part_item_row = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_item_row);
  327. part_item_icon = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_item_icon);
  328. part_item_label = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_item_label);
  329. part_item_check = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_item_check);
  330. part_separator = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_separator);
  331. part_submenu_arrow = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_submenu_arrow);
  332. part_item_accel_label = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_item_accel_label);
  333. part_menubar_joiner = CL_GUIThemePart(this, CssStr::PopupMenuWindow::part_menubar_joiner);
  334. icon_column_width = part_component.get_property_int(prop_icon_column_width);
  335. icon_size = part_item_icon.get_preferred_size();
  336. check_size = part_item_check.get_preferred_size();
  337. func_render().set(this, &CL_PopupMenuWindow::on_render);
  338. }